The package list you need to install and load for the workflow:

rm(list=ls())
cran_packages   <- c("knitr", "phyloseqGraphTest", "phyloseq", "shiny", "microbiome",
                     "tidyverse", "miniUI", "caret", "pls", "e1071", "ggplot2",
                     "randomForest","entropart", "vegan", "plyr", "dplyr","here", "ggrepel", "nlme", 
                     "R.utils", "gridExtra", "googledrive", "googlesheets", "phangorn", "devtools", 
                     "rmarkdown", "sys","picante","reshape2", "devtools", "PMA","structSSI","ade4", 
                     "ape", "Biostrings", "igraph", "ggnetwork", "intergraph", "ips","scales", "kableExtra", 
                     "pgirmess","treemap", "ggpubr", "rstatix", "ggthemes", "ggpubr","outliers", "ICC",
                     "moments", "cowplot", "pgirmess", "dunn.test", "viridis", "grid", "AICcmodavg", "raster", 
                     "FactoMineR", "PerformanceAnalytics", "Hmisc", "DHARMa", "ade4", "car","smplot",
                     "labdsv","epitools","ggsignif", "biomformat","randomcoloR", "lubridate","dichromat",
                     "hillR", "adespatial", "DT")

github_packages <- c("jfukuyama/phyloseqGraphTest",'smin95/smplot', "gauravsk/ranacapa", "jfq3/QsRutils", "pmartinezarbizu/pairwiseAdonis/pairwiseAdonis")
github_lib <- c("phyloseqGraphTest","smplot", "ranacapa","QsRutils", "pairwiseAdonis")
bioc_packages   <- c("phyloseq", "genefilter", "impute", "dada2", "DECIPHER", "ggtree")
knitr::opts_chunk$set(eval = T)
# Load libraries
sapply(c(cran_packages, bioc_packages, github_lib), require, character.only = TRUE)
detach("package:dplyr", unload = TRUE)
detach("package:plyr", unload = TRUE)
library(plyr)
library(dplyr)
set.seed(1000)

Prepare your working directory

knitr::opts_knit$set(root.dir = getwd())
path = getwd()
# This will setwd to wherever the .Rmd file is opened.
dir_dataset <- paste0(path,"/dir_dataset/")
dir_graph <- paste0(path,"/dir_graph/")
dir_fastq_source <- paste0(dir_dataset,"sequences/")
dir_refdb   <- paste0(dir_dataset, "reference_databases/")
dir_filtered <- paste0(dir_dataset,"sequences/filtered/")
dir.create(dir_dataset, recursive = T); dir.create(dir_graph, recursive = T);dir.create(dir_filtered, recursive = TRUE) ; dir.create(dir_refdb, recursive = TRUE)

Note

This document is interactive. You can sort and scroll through most of the tables and figures. In the upper right hand corner of the front page is a Code button. Use this to show or hide all the code in the document (default is hide) as well as download the .Rmd file which you can use to extract the code.

Abbreviations

  • Aggregation Score (AS): Occurrence (the number of counts observed in the aggregation chamber) for 24h or 72h observation.

  • Amplicon Sequence Variant (ASV): Exact sequence variant—analogous to an OTU—but with single nucleotide resolution.

Study goals

  1. Testing an operating system of a multiple high-sensitive cameras for 72h capturing the simultaneous positions of 66 females F. auricularia (Van Meyel et al., (2022)).

  2. Confirm the repeatability of the aggregation level at individual scale (i.e., over 72h) and between individual (i.e., between independent weeks of experiments).

  3. Analyse the potential influence of gut microbiota on the level of aggregation through microbial diversity comparisons.

  4. Perform a gut transplant experiment in a 2x2 full-factorial design, in which we fed high- or low-aggregation females with the gut of high- or low- aggregation females and then measured the aggregation score of the recipient females.


Workflow overview

Part I: Data preparation

The data are located in three files available in Supplementary Material of the article.

  • aggregation_data : The aggregation_data contains information of all individuals for each hour with further biological measures.

  • ps_rff : The phyloseq object already normalized at 10 180 sequences (i.e., the minimum sample sum of the dataset) for a final dataset constituted by 397 020 sequences (1 593 ASVs) for 19 low- and 20 high-aggregation females (one sample did not amplify).

  • transplant_data : The data concerning the individual involved in the gut transplant with their AS before the gut transplant (for donors and recipients).

  • aggregation_after_transplant : The data concerning the recipients individual aggregation data for each hour.

In case you want to process the reads from your own, see Part III: Microbiota diversity influence on Aggregation level.

Part II: Variability intra/inter individuals

We first tested whether the number of females’ occurrences in the aggregation chamber (i.e., Aggregation Score) was repeatable over the three days of measurements and/or overall changed throughout the six recording sessions (from the 10 June 2022 to the 25 July 2022). To this end, we conducted a series of three Linear Models (LMs), in which we entered the Aggregation Score of each day as the response variable, and the Aggregation Score of one of the two other day (continuous), the week of the recording session (category) and the interaction as explanatory variables.

Ps: For each statistical model, we checked that all assumptions were fulfilled using the DHARMa R package v0.4.6 (Hartig, 2022).

Part III: Microbiota diversity and Aggregation level

This part allows to perform a descriptive analysis of the microbiota from the ps_rff object and to further calculate both alpha and beta diversity taking into account the taxonomy and the phylogeny, for quantitative and qualitative informations from microbial communities.

Part IV: Gut transplantation

Last, this part concerns the gut transplant experiment in which we fed high- or low-aggregation females with the gut of high- or low- aggregation females and then measured the aggregation score of the recipient females.


Part I: Data preparation

back to top

Load the files

First, download the metadata file in the /dir_dataset folder and load it to create a new table with Aggregation Score for each sample at each experimental day (D1 = day1, D2 = day2 and D3 = day3).

aggregation_data <- read.table(file = paste0(dir_dataset, "aggregation_data.txt"), header = T, sep = "\t")
AS_data <- left_join(
  aggregation_data %>% 
    group_by(ID,day,sampling_date) %>% 
    summarise(Aggregation = sum(attraction)) %>%
    pivot_wider(names_from = day, values_from = Aggregation) %>%
    mutate(AS72 = sum(c_across(D1:D3))) %>%
    as.data.frame(),
  aggregation_data %>% 
    select(!c(sampling_date, attraction, day, time_exp)) %>% 
    unique(),
  by = "ID")

write.table(AS_data,file = paste0(dir_dataset, "AS_data.txt"), sep ="\t", row.names = F)

Presentation of the data

The aggregation_data is constituted * ID : The ID of the female sample.

  • sampling_date: Sampling date is associated to a run number.

  • attraction: Presence (1) or absence (0) in the aggregation chamber at the observation t.

  • time_exp: Observation number (between 1 and 72)

  • day: The day number or the experiment (D1,D2 or D3) allowing to calcul the AS at D1, D2 and D3 in the AS_data.

  • aggregation: the female is very stuck to the attractive chamber (Y = yes) or not (N = no) when she is in the aggregation chamber. The total over the 72 observations allows to obtain the AS72 in the AS_data.

AS_data <- read.table(file = paste0(dir_dataset, "AS_data.txt"), header = T, sep = "\t")
datatable(AS_data %>%
            filter(status %in% c("High","Low")),
          rownames = FALSE, 
          width = "100%",
          colnames = c("ID", "Sampling date","AS at D1", "AS at D2", "AS at D3", "AS over 72h", "Aggregation level"), 
          caption = htmltools::tags$caption(style = "caption-side: 
                                            bottom; text-align: left;", 
                                            "Table: ", 
                                            htmltools::em("short table presentation for the females presenting low- and high-aggregation levels.")), 
          extensions = "Buttons", 
          options = list(columnDefs = 
                           list(list(className = "dt-left", targets = 0)), 
                         initComplete = JS("function(settings, json) {",
                                           "$(this.api().table().header()).css({'background-color': '#000', 'color': '#fff'});
                                           $('body').css({'font-family': 'Calibri'});
                                           $('body').css({'font-color': '#fff'});",
                                           "}"),
                         dom = "Blfrtip", pageLength = 5, 
                         lengthMenu = c(5, 10, 25, 50), 
                         buttons = c("csv", "copy"), 
                         scrollX = TRUE, scrollCollapse = TRUE)) %>%
  formatStyle("ID", backgroundColor = 'white')%>%
  formatStyle("sampling_date", backgroundColor = 'white')%>%
  formatStyle("D1", backgroundColor = 'white')%>%
  formatStyle("D2", backgroundColor = 'white')%>%
  formatStyle("D3", backgroundColor = 'white')%>%
  formatStyle("AS72", backgroundColor = 'white')%>%
  formatStyle("status", backgroundColor = 'white')

Part II: Variability intra/inter individuals

back to top

Aggregation distribution

We illustrated the aggregation between individuals in 385 female earwigs to determine empirically the variability of this behaviour and its constant in time under same environmental conditions (here, the same 3 attractants females).

AS_data <- read.table(file = paste0(dir_dataset, "AS_data.txt"), header = T, sep = "\t")

Fig_AS72 <- AS_data %>%
  ggplot(aes(x=AS72)) +
  geom_histogram(aes(y = after_stat(density)),alpha = 0.5, bins = 72 ,color = "black",linewidth = 0.5) +
  theme_bw() +
  theme(axis.text = element_text(family = "serif",size = 12),
        axis.text.x = element_text(family = "serif",size = 12),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        axis.title.x = element_text(size=14, family = "serif"),
        axis.title.y = element_text(size=14, family = "serif"),
        legend.position = "none") +
  xlab("Aggregation Score") +
  ylab("Frequency")
Fig_AS72

pdf(file = paste0(dir_graph, "Fig_AS72.pdf"), he = 7, wi = 7)
Fig_AS72
dev.off()

We test if the aggregation is a deterministic or random trait by comparing the distribution with a random distribution of AS at 72h we compared to our observed AS72 with a Khi2 test (i.e., aka 1/3 chance of 72 chance to be in the aggregation chamber). This confirm the fact that the behaviour of the female is not random.

AS_data <- read.table(file = paste0(dir_dataset, "AS_data.txt"), header = T, sep = "\t")
aggregation_data <- read.table(file = paste0(dir_dataset,"aggregation_data.txt"), header = T, sep = "\t")
AS <- c(0,1)
AS_th <- sample(AS, size = 72*385, replace = T, p= c(2/3,1/3)) # if random, 2/3 chance to be in the non-aggregative chamber and 1/3 to be in the aggregative chamber
aggregation_th <- aggregation_data
aggregation_th$attraction = AS_th

AS_data_th <- aggregation_th %>% 
  group_by(ID,day,sampling_date) %>% 
  summarise(Aggregation = sum(attraction)) %>%
  pivot_wider(names_from = day, values_from = Aggregation) %>%
  mutate(AS72 = sum(c_across(D1:D3))) %>%
  as.data.frame()
## `summarise()` has grouped output by 'ID', 'day'. You can override using the
## `.groups` argument.
comp <- cbind("AS_obs" = AS_data$AS72, 
              "AS_th" = AS_data_th$AS72) %>%
  t() %>%
  data.frame()

colnames(comp) <- AS_data$ID
chisq.test(comp)
## 
##  Pearson's Chi-squared test
## 
## data:  comp
## X-squared = 2150.9, df = 384, p-value < 2.2e-16

Aggregation repeatability

To confirm the robustness of our aggregation score (AS), we first analysed the repeatability of this value over the three days on which measurements were taken using three Linear models (LM). In these models, the response variable was the AS measured either on day 2, 3 or 3, whereas the explanatory variables were the AS measured on day 1, 2 or 1, respectively, the week of the recording session (six sessions: categorical factor) and the interaction between these two variables.

Ps: For each statistical model, we checked that all assumptions were fulfilled using the DHARMa R package v0.4.6 (Hartig, 2022). Here is an example for the LM run on the D2 and D1.

AS_data <- read.table(file = paste0(dir_dataset, "AS_data.txt"), sep ="\t", header = T)

lm_D2D1 <- lm(D2 ~ D1 * sampling_date , data = AS_data)
simulationOutput_D2D1 <- simulateResiduals(fittedModel = lm_D2D1, plot = T)

testResiduals(lm_D2D1)

As such, we tested either the AS at D2 was explained by the AS at D1 whatever the sampling run…

AS_data <- read.table(file = paste0(dir_dataset, "AS_data.txt"), sep ="\t", header = T)

lm_D2D1 <- lm(D2 ~ D1 * sampling_date , data = AS_data)
car::Anova(lm_D2D1)
## Anova Table (Type II tests)
## 
## Response: D2
##                   Sum Sq  Df F value Pr(>F)    
## D1                7531.9   1 221.935 <2e-16 ***
## sampling_date      247.9   5   1.461 0.2018    
## D1:sampling_date   264.7   5   1.560 0.1705    
## Residuals        12658.8 373                   
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Either the AS at D3 was explained by the AS at D2 whatever the sampling run…

AS_data <- read.table(file = paste0(dir_dataset, "AS_data.txt"), sep ="\t", header = T)

lm_D3D2 <- lm(D3 ~ D2 * sampling_date , data = AS_data)
car::Anova(lm_D3D2)  
## Anova Table (Type II tests)
## 
## Response: D3
##                   Sum Sq  Df  F value Pr(>F)    
## D2                9665.5   1 280.7716 <2e-16 ***
## sampling_date       34.6   5   0.2013 0.9618    
## D2:sampling_date   104.5   5   0.6072 0.6944    
## Residuals        12840.4 373                    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

And finally either the AS at D3 was explained by the AS at D1 whatever the sampling run.

AS_data <- read.table(file = paste0(dir_dataset, "AS_data.txt"), sep ="\t", header = T)

lm_D3D1 <- lm(D3 ~ D1 * sampling_date , data = AS_data)
car::Anova(lm_D3D1)
## Anova Table (Type II tests)
## 
## Response: D3
##                   Sum Sq  Df F value Pr(>F)    
## D1                3924.1   1 79.6472 <2e-16 ***
## sampling_date      120.5   5  0.4891 0.7844    
## D1:sampling_date   309.0   5  1.2544 0.2831    
## Residuals        18377.2 373                   
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
AS_data <- read.table(file = paste0(dir_dataset, "AS_data.txt"), sep ="\t", header = T)
### _ Between D1 and D2 ----
D1_D2_runs <- AS_data %>%
  ggplot(aes(x = D2, y = D1, color = sampling_date)) +
  geom_point(aes(color = sampling_date), alpha = 0.5) +
  geom_smooth(method= lm, se=T, fullrange=T) +
  ggpubr::color_palette("ucscgb")+
  ylab("Aggregation Score (Day 2)") +
  xlab("Aggregation Score (Day 1)") +
  theme_bw() +
  theme(axis.text = element_text(family = "serif",size = 16), 
        axis.text.x = element_text(family = "serif",size = 16),
        axis.text.y = element_text(family = "serif",size = 16),
        axis.title.x = element_text(family = "serif",size = 18),
        axis.title.y = element_text(family = "serif",size = 18),
        legend.text = element_text(family = "serif", size = 16),
        legend.title = element_text(family = "serif", size = 18),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        legend.position = "none")

### _ Between D1 and D3 ----
D1_D3_runs <- AS_data %>%
  ggplot(aes(x = D1, y = D3, color = sampling_date)) +
  geom_point(aes(color = sampling_date), alpha = 0.5) +
  geom_smooth(method= lm, se=T, fullrange=T) +
  ggpubr::color_palette("ucscgb")+
  ylab("Aggregation Score (Day 3)") +
  xlab("Aggregation Score (Day 1)") +
  theme_bw() +
  theme(axis.text = element_text(family = "serif",size = 16), 
        axis.text.x = element_text(family = "serif",size = 16),
        axis.text.y = element_text(family = "serif",size = 16),
        axis.title.x = element_text(family = "serif",size = 18),
        axis.title.y = element_text(family = "serif",size = 18),
        legend.text = element_text(family = "serif", size = 16),
        legend.title = element_text(family = "serif", size = 18),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        legend.position = "none")

### _ Between D2 and D3 ----
D2_D3_runs <- AS_data %>%
  ggplot(aes(x = D2, y = D3, color = sampling_date)) +
  geom_point(aes(color = sampling_date), alpha = 0.5) +
  geom_smooth(method= glm, se=T, fullrange=T) +
  ggpubr::color_palette("ucscgb")+
  ylab("Aggregation Score (Day 3)") +
  xlab("Aggregation Score (Day 2)") +
  theme_bw() +
  theme(axis.text = element_text(family = "serif",size = 16), 
        axis.text.x = element_text(family = "serif",size = 16),
        axis.text.y = element_text(family = "serif",size = 16),
        axis.title.x = element_text(family = "serif",size = 18),
        axis.title.y = element_text(family = "serif",size = 18),
        legend.text = element_text(family = "serif", size = 16),
        legend.title = element_text(family = "serif", size = 18),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        legend.position = "none")

runs_legend <- AS_data %>%
  ggplot(aes(x = D2, y = D3, color = sampling_date)) +
  geom_point(aes(color = sampling_date), alpha = 0.5) +
  geom_smooth(method= glm, se=T, fullrange=T) +
  ggpubr::color_palette("ucscgb")+
  ylab("Aggregation Score (Day 3)") +
  xlab("Aggregation Score (Day 2)") +
  theme_bw() +
  theme(axis.text = element_text(family = "serif",size = 16), 
        axis.text.x = element_text(family = "serif",size = 16),
        axis.text.y = element_text(family = "serif",size = 16),
        axis.title.x = element_text(family = "serif",size = 18),
        axis.title.y = element_text(family = "serif",size = 18),
        legend.text = element_text(family = "serif", size = 16),
        legend.title = element_text(family = "serif", size = 18),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank())+
  labs(color = "Sampling week")

Fig_repeat <- ggarrange(legend.grob = get_legend(runs_legend),
          legend = "right",
          D1_D2_runs,D1_D3_runs,D2_D3_runs,
          nrow = 1, ncol = 3)
Fig_repeat

pdf(file = paste0(dir_graph, "Fig_repeat.pdf"), he = 5, wi = 16)
Fig_repeat
dev.off()

Part III: Microbiota diversity and Aggregation level

back to top

This is the bioinformatical pipeline to process the libraries originating from the gut microbiota

The libraries used in this study (and available in NCBI under the bioproject accession no. PRJNA936136). Before starting, libraries had to be demultiplexed and linkers with primers sequences removed before being processed through DADA2 pipeline (Callahan et al., 2016). Briefly, sequences are trimmed and filtered for quality, dereplicated and inferred in ASVs (Glassman & Martiny, 2018). Forward and reverse ASVs are merged and finally generated into a count table where chimera are identified and removed. The taxonomy assignment is performed using the SILVA reference database (release 138) (Quast et al., 2013). A phyloseq object (McMurdie & Holmes, 2013) is generated for further statistical analyses.

DADA2 process

First, you need to load the original R1 R2 reads folder in the /dir_dataset/dir_fastq_source folder. Then, download the SILVA database references into the /dir_dataset/dir_ref_db folder.

In this study, we used the primers set 343F - 794R specific for the region V3V4 of the prokaryotic 16S rDNA (Muyzer, 1993).

primer_F_343 = "ACGGRAGGCAGCAG"
primer_R_784 = "TACCAGGGTATCTAATC"

Read the folders to be sure all files are in it and that direction is well indicated. Get the list of your samples and extract the sample names:

nms_seq_runs  <- list.files(dir_fastq_source)
paths_seq_runs <- list.files(dir_fastq_source, full.names = TRUE) %>% setNames(nms_seq_runs)

nms_refdb   <- list.files(dir_refdb)
paths_refdb <- list.files(dir_refdb, full.names = TRUE) %>% setNames(nms_refdb)

fns <- sort(paths_seq_runs)
fns <- fns[str_detect(basename(fns), ".fastq")]
fns_R1 <- fns[str_detect(basename(fns), "R1_001.fastq")]
fns_R2 <- fns[str_detect(basename(fns), "R2_001.fastq")]
if(length(fns_R1) != length(fns_R2)) stop("Forward and reverse files do not match.")

sample_names <- sapply(strsplit(basename(fns_R1), "_"), `[`, 1)
sample_names

Once sample names cut, you can remove the primer sequence from your data by “counting” the number of nucleotides of each primer. Because nucleotides maybe me flowing, it not advised to remove the sequence of the primer by its nucleotide composition.

primer_length_fwd = str_length(primer_F_343)
primer_length_rev = str_length(primer_R_784)

Quality profiles and filter/trim the sequences

Now you can plot the quality profiles of your reads (which is usely better on the R1) in order to truncate the sequences before quality drastically decrease. Then, you can filter your sequences and trim the primer length.

qual_R1 <- plotQualityProfile(fns_R1[1])
qual_R2 <- plotQualityProfile(fns_R2[1])
ggsave(qual_R1, file = paste0(dir_graph , "quality_profile_R1.png"))
ggsave(qual_R2, file = paste0(dir_graph, "quality_profile_R2.png"))
filt_R1 <- str_c(dir_filtered, sample_names, "_R1_filt.fastq")
filt_R2 <- str_c(dir_filtered, sample_names, "_R2_filt.fastq")
names(filt_R1) <- sample_names
names(filt_R2) <- sample_names
set.seed(1000)
out <- filterAndTrim(fns_R1, filt_R1, fns_R2, filt_R2, truncLen=c(240,240),
                     maxN=0, maxEE=c(2,2), truncQ=2, trimLeft=c(primer_length_fwd,primer_length_rev) , rm.phix=TRUE,
                     compress=TRUE, multithread=TRUE)
head(out)

sample_names <- sapply(strsplit(basename(filt_R1), "_"), `[`, 1) # Assumes filename = samplename_XXX.fastq.gz
sample_namesR <- sapply(strsplit(basename(filt_R2), "_"), `[`, 1) # Assumes filename = samplename_XXX.fastq.gz
if(!identical(sample_names, sample_namesR)) stop("Forward and reverse files do not match.")
names(filt_R1) <- sample_names
names(filt_R2) <- sample_names
set.seed(1000)

Finally, you can learn the error rates for both reads and save them.

errF <- learnErrors(filt_R1,nbases=1e8, multithread=TRUE)
errR <- learnErrors(filt_R2, nbases=1e8, multithread=TRUE)
plotErrors_F <- plotErrors(errF, nominalQ=TRUE)
plotErrors_R <- plotErrors(errR, nominalQ=TRUE)
ggsave(plotErrors_F, file = paste0(dir_graph , "plotErrors_F.png"))
ggsave(plotErrors_R, file = paste0(dir_graph, "plotErrors_R.png"))

Dereplication and merging sequences

Now, we dereplicate the filtered reads in order to infer them in dada file to merge.

mergers <- vector("list", length(sample_names))
names(mergers) <- sample_names
for(sam in sample_names) {
  cat("Processing:", sam, "\n")
  derepFs <- derepFastq(filt_R1[[sam]])
  ddF <- dada(derepFs, err=errF, multithread=TRUE)
  derepRs <- derepFastq(filt_R2[[sam]])
  ddR <- dada(derepRs, err=errR, multithread=TRUE)
  merger <- mergePairs(ddF, derepFs, ddR, derepRs)
  mergers[[sam]] <- merger
}

rm(derepFs); rm(derepRs)

Now, we can construct our sequence table “seqtab” and remove chimeras. It is recommandable to track the reads through the pipeline process

seqtab <- makeSequenceTable(mergers)
row.names(seqtab) = sample_names
dim(seqtab)
table(nchar(getSequences(seqtab)))

seqtab.nochim <- removeBimeraDenovo(seqtab, method="consensus", multithread=TRUE, verbose=TRUE)
dim(seqtab.nochim)
sum(seqtab.nochim)/sum(seqtab)
saveRDS(seqtab.nochim, paste0(dir_filtered ,"seqtab.nochim.rds"))

# ____ Track reads through the pipeline ----
getN <- function(x) sum(getUniques(x))
track <- cbind(out, sapply(dadaFs, getN), sapply(mergers, getN), rowSums(seqtab), rowSums(seqtab.nochim))
colnames(track) <- c("input", "filtered", "denoised", "merged", "tabled", "nonchim")
rownames(track) <- sample_names
head(track)
save(track, file = paste0(dir_filtered, "track_343F-784R.RData")

Taxonomy assignment

From the SILVA ref files previously deposited in the dir_refdb folder, assign the sequence tables

path_reference_db <- paste0(dir_refdb, "/silva_nr_v138_train_set.fa.gz")
path_species_db   <- paste0(dir_refdb, "/silva_species_assignment_v138.fa")
taxaRC <- assignTaxonomy(seqtab.nochim, path_reference_db, tryRC=TRUE)
taxaSp <- addSpecies(taxaRC, path_species_db) # This step can be very long

taxa.print <- taxaSp # Removing sequence rownames for display only
rownames(taxa.print) <- NULL
head(taxa.print)

Phyloseq creation

Now you can create your phyloseq object with your filtered and assigned sequences.

ps <- phyloseq(otu_table(seqtab.nochim, taxa_are_rows=FALSE),tax_table(taxaSp)) 
dna <- Biostrings::DNAStringSet(taxa_names(ps))
names(dna) <- taxa_names(ps)
ps <- merge_phyloseq(ps, dna)
taxa_names(ps) <- paste0("ASV", seq(ntaxa(ps)))
ps
save(ps, file = paste0(dir_filtered, "ps.RData"))

Free r stack memory in order to pursue the analysis. Don’t forget to reload all the library packages and reassign the pathways

Environmental data table merging

The envdata is constituted of the sampling data from the AS_data previously created. This step is necessary to homogenize the names of the phyloseq object with the envdata. First, change the ID names to be congruent with the names of the sequences.

AS_data <- read.table(file = paste0(dir_dataset, "AS_data.txt"), sep ="\t", header = T)
AS_data$ID <- str_replace_all(AS_data$ID,
                                        c("MS." = "MS-","MC." = "MC-"))
AS_data$ID <- paste0(AS_data$ID , "-G")

env_dat <- AS_data %>% filter(ID %in% sample_names(ps))
env_dat <- sample_data(env_dat)
sample_names(env_dat) <- env_dat$ID

sort(sample_names(env_dat)) == sort(sample_names(ps))

Now that the seqtab and the envdata are corresponding,merge them into phyloseq object.

ps1 <- merge_phyloseq(ps, env_dat)
ps1
save(ps1, file = paste0(dir_filtered, "ps1.RData"))

Plot the sample minimum ASV

load(file = paste0(dir_filtered, "ps1.RData"))

paste0("The minimum sample sum of the dataset is ",min(rowSums(ps1@otu_table@.Data)))
## [1] "The minimum sample sum of the dataset is 10299"
readsumsdf = data.frame(nreads = sort(taxa_sums(ps1),TRUE), sorted = 1:ntaxa(ps1), type = "ASVs")
readsumsdf = rbind(readsumsdf, data.frame(nreads = sort(sample_sums(ps1), TRUE), sorted = 1:nsamples(ps1), type = "Samples"))
p = ggplot(readsumsdf, aes(x = sorted, y = nreads)) + geom_bar(stat = "identity")
p + ggtitle("Total number of reads") + scale_y_log10() + facet_wrap(~type, 1, scales = "free")

Normalization and calibration

Filter the ps1 to only keep the Prokaryotes

ps_proka <-  subset_taxa(ps1, Kingdom %in% c("Archaea", "Bacteria") & 
                           Order != "Chloroplast" & 
                           Family != "Mitochondria")
save(ps_proka, file = paste0(dir_filtered,"ps_proka.RData"))

Normalization of all samples at 10 180 sequences (i.e., the minimum sample sum of the dataset) for a final dataset constituted by 397 020 sequences.

Each time the function rarefy_even_depth() is ran, it randomly creates a new rarefies phyloseq with the sequences entered in the function. That means that with the same function and argument, the ps_rff will be with the same length (same number of sequences) but might be different in term of quality of sequences (not the same number of ASVs).

load(file = paste0(dir_filtered,"ps_proka.RData"))
paste0("The minimum sample sum for the prokaryote sequences is ",min(rowSums(ps_proka@otu_table@.Data)))
## [1] "The minimum sample sum for the prokaryote sequences is 10180"
ps_rff <- prune_samples(sample_sums(ps_proka) >= min(sample_sums(ps_proka)) , ps_proka) # normalized at 10180 seq/sample
ps_rff <- rarefy_even_depth(ps_proka, sample.size = min(sample_sums(ps_proka)))
cov_rff <- goods(ps_rff@otu_table@.Data)
paste0("The coverage after normalization is ", round(mean(cov_rff$goods),2),"% +- " ,round(sd(cov_rff$goods),2))
## [1] "The coverage after normalization is 99.96% +- 0.03"
paste0("The ratio between the initial set and the normalized data ",round(taxa_sums(ps_rff) %>% names() %>% length() / taxa_sums(ps_proka) %>% names() %>% length(), 2), "%")
## [1] "The ratio between the initial set and the normalized data 0.96%"
tax_table(ps_rff) <- cbind(tax_table(ps_rff),"ASV"= tax_table(ps_rff)) # Add a new colomun with the ASV Id
#save(ps_rff, file = paste0(dir_dataset,"ps_rff.RData")) # If you run and save the ps_rff each time, your file will change and the further analyses too (even if slightly).  

To be sure that the minimal abundance of the data set is enough to capture all the diversity, we use the function ggrare()

ggrare <- function(physeq_object, step = 10, label = NULL, color = NULL, plot = TRUE, parallel = FALSE, se = TRUE) {
  x <- methods::as(phyloseq::otu_table(physeq_object), "matrix")
  if (phyloseq::taxa_are_rows(physeq_object)) { x <- t(x) }
## This script is adapted from vegan `rarecurve` function
  tot <- rowSums(x)
  S <- rowSums(x > 0)
  nr <- nrow(x)
  rarefun <- function(i) {
    cat(paste("rarefying sample", rownames(x)[i]), sep = "\n")
    n <- seq(1, tot[i], by = step)
    if (n[length(n)] != tot[i]) {
      n <- c(n, tot[i])
    }
    y <- vegan::rarefy(x[i, ,drop = FALSE], n, se = se)
    if (nrow(y) != 1) {
      rownames(y) <- c(".S", ".se")
      return(data.frame(t(y), Size = n, Sample = rownames(x)[i]))
    } else {
      return(data.frame(.S = y[1, ], Size = n, Sample = rownames(x)[i]))
    }
  }
  if (parallel) {
    out <- parallel::mclapply(seq_len(nr), rarefun, mc.preschedule = FALSE)
  } else {
    out <- lapply(seq_len(nr), rarefun)
  }
  df <- do.call(rbind, out)
  # Get sample data
  if (!is.null(phyloseq::sample_data(physeq_object, FALSE))) {
    sdf <- methods::as(phyloseq::sample_data(physeq_object), "data.frame")
    sdf$Sample <- rownames(sdf)
    data <- merge(df, sdf, by = "Sample")
    labels <- data.frame(x = tot, y = S, Sample = rownames(x))
    labels <- merge(labels, sdf, by = "Sample")
  }
  # Add, any custom-supplied plot-mapped variables
  if ( length(color) > 1 ) {
    data$color <- color
    names(data)[names(data) == "color"] <- deparse(substitute(color))
    color <- deparse(substitute(color))
  }
  if ( length(label) > 1 ) {
    labels$label <- label
    names(labels)[names(labels) == "label"] <- deparse(substitute(label))
    label <- deparse(substitute(label))
  }
  p <- ggplot2::ggplot(data = data,
                       ggplot2::aes_string(x = "Size",
                                           y = ".S",
                                           group = "Sample",
                                           color = color))
  p <- p + ggplot2::labs(x = "Sequence Sample Size", y = "Species Richness")
  if (!is.null(label)) {
    p <- p + ggplot2::geom_text(data = labels,
                                ggplot2::aes_string(x = "x",
                                                    y = "y",
                                                    label = label,
                                                    color = color),
                       size = 4, hjust = 0)
  }
  p <- p + ggplot2::geom_line()
  if (se) { ## add standard error if available
    p <- p +
      ggplot2::geom_ribbon(ggplot2::aes_string(ymin = ".S - .se",
                                               ymax = ".S + .se",
                                               color = NULL,
                                               fill = color),
                           alpha = 0.2)
  }
  if (plot) {
    plot(p)
  }
  invisible(p)
}
load(file = paste0(dir_dataset,"ps_rff.RData"))
ggrare(ps_rff, step = 500,  label = "ID", se = F, color = "status", plot = F) + 
  theme_bw() +
  scale_color_manual(values = c("darkblue" , "darkred"), 
                     labels=c('Low-aggregation', 'High-aggregation')) +
  theme(axis.title = element_text(family = "serif", size = 12), 
        axis.text = element_text(family = "serif"), 
        axis.text.x = element_text(family = "serif",size = 12), 
        axis.text.y = element_text(family = "serif",size = 12), 
        axis.line = element_line(size = 0), 
        panel.background = element_blank(),
        plot.title = element_text(family = "serif"),
        legend.text = element_text(size = 12,  family = "serif"), 
        legend.title = element_text(size = 12, family = "serif"))+
  labs(colour = "Aggregation levels")

Phylogenetic calibration —-

Because we used phylogenetic indices that take into account the phylogenetic distance between taxa, we need to calibrate and insert the phhylogenetic tree of the phyloseq object. To do that, create the ps_rff.fasta (a note file that contains all the ASVs with their successive nucleotidic sequences).

Biostrings::writeXStringSet(ps_rff@refseq, file = "ps_rff.fasta") #create fasta file

You also need the ps_rff.biom file which is the output format when you proceed your librairies in the Qiime2 pipeline.

Here is a function that allows to transform the otu matrix in a biom file.

make_biom <- function(data, sample_metadata=NULL, observation_metadata=NULL, id=NULL, matrix_element_type="int"){
  # The observations / features / OTUs / rows "meta" data table
  if(!is.null(observation_metadata)){
    rows = mapply(list, SIMPLIFY=FALSE, id=as.list(rownames(data)),
                  metadata=alply(as.matrix(observation_metadata), 1, .expand=FALSE, .dims=TRUE))
  } else {
    rows = mapply(list, id=as.list(rownames(data)), metadata=NA, SIMPLIFY=FALSE)
  }
  # The samples / sites / columns "meta" data table
  if(!is.null(sample_metadata)){
    columns = mapply(list, SIMPLIFY=FALSE, id=as.list(colnames(data)),
                     metadata=alply(as.matrix(sample_metadata), 1, .expand=FALSE, .dims=TRUE)) 
  } else {
    columns = mapply(list, id=as.list(colnames(data)), metadata=NA, SIMPLIFY=FALSE)
  }
  # Convert the contingency table to a list
  datalist = as.list(as.data.frame(as(t(data), "matrix")))
  names(datalist) <- NULL
  # Define the list, instantiate as biom-format, and return
  # (Might eventually expose some of these list elements as function arguments)
  format_url = "http://biom-format.org"
  return(biom(list(id=id,
                   format = "Biological Observation Matrix 1.0.0",
                   format_url = format_url,
                   type = "OTU table",
                   generated_by = sprintf("biomformat %s", packageVersion("biomformat")),
                   date = as.character(Sys.time()),
                   matrix_type = "dense",
                   matrix_element_type = matrix_element_type,
                   shape = dim(data),
                   rows = rows,
                   columns = columns,
                   data = datalist)
  ))
}

Write the biom file and run both the fasta and the biom files in the Galaxy server. The multiple alignment of the sequences was provided with the MAFFT program (Katoh, 2002) and we inferred the phylogenetic tree with the FastTree 2 tool (Price et al., 2010) and Phangorn package v2.8.1 (Schliep, 2011).

otu <-t(as(otu_table(ps_rff),"matrix")) # 't' to transform if taxa_are_rows=FALSE
biom <- make_biom(data= otu)
write_biom(biom,"biom") #create biom file
#Run both files in Galaxy Frogs to construct the phylogenetic tree

The output delivered by the server in a newick file that migh be include now in the phyloseq object. > Note that this step can be made with the prokaryotic phyloseq object but the process will be longer. It can be an heavy task to run depending on the weight of your fasta file. Here is a very small dataset of sequences.

tree <- ape::read.tree(paste0(dir_dataset, "tree.nwk"))
ps_rff <- merge_phyloseq(ps_rff, tree)
save(ps_rff, file = paste0(dir_dataset,"ps_rff.RData"))

OTUs clustering

This step allows to quickly obtain OTU with 88%, 93% and 97% clustering thresholds and create new phyloseq objects with these forming OTUs. See

nproc <- 4 # set to number of cpus/processors to use for the clustering
dna <- refseq(ps_rff)
## Find clusters of ASVs to form the new OTUs
aln <- DECIPHER::AlignSeqs(dna, processors = nproc)
d <- DECIPHER::DistanceMatrix(aln, processors = nproc)

clusters <- DECIPHER::TreeLine(
  myDistMatrix=d,
  method = "complete",
  cutoff = c(0.12, 0.07,0.03), # 88%, 93% and 97% OTU clustering
  type = "clusters",
  processors = nproc)

## Use dplyr to merge the columns of the seqtab matrix for ASVs in the same OTU
# prep by adding sequences to the `clusters` data frame
clusters <- clusters %>%  add_column(sequence = asv_sequences)
ps_97 <- merge_taxa_vec(
  ps_rff, 
  group = clusters$cluster0_03complete,
  tax_adjust = 2
)

ps_93 <- merge_taxa_vec(
  ps_rff, 
  group = clusters$cluster0_07complete,
  tax_adjust = 2
)

ps_88 <- merge_taxa_vec(
  ps_rff, 
  group = clusters$cluster0_12complete,
  tax_adjust = 2
)

save(ps_97,ps_93, ps_88, file = paste0(dir_dataset, "ps_OTUs.RData"))

Microbial composition

Create a data table physeq from the phyloseq object that will be used to construct barplot

load(file = paste0(dir_dataset, "ps_rff.RData"))
physeq <- ps_rff %>%
  tax_glom(taxrank = "ASV") %>%                     # agglomerate at phylum level
  transform_sample_counts(function(x) {x/sum(x)} ) %>% # Transform to rel. abundance
  psmelt() 

physeq$ID <- factor(physeq$ID, levels = unique(arrange(physeq, physeq$AS72)$ID))
physeq$Abundance <- physeq$Abundance/sum(physeq$Abundance) * 100 # To obtain %
write.table(physeq, file = paste0(dir_dataset, "physeq.txt"), sep ="\t", row.names = F)

Create two barplots for both microbial Phyla and Families.

Phylum composition

physeq <- read.table(file = paste0(dir_dataset, "physeq.txt"), sep ="\t", header = T)

compo_phylum <- physeq %>%
  group_by(Phylum) %>%
  summarise(Prop_phylum = sum(Abundance)) %>%
  arrange(desc(Prop_phylum))

physeq$Phylum <- factor(physeq$Phylum, levels = rev(as.character(compo_phylum$Phylum)))

Fig_phylum <- physeq %>%
  group_by(ID, Phylum) %>%
  summarise(Prop_phylum = sum(Abundance)) %>%
  ggplot(aes(x = ID, y = Prop_phylum, fill = Phylum)) + 
  geom_bar(stat="identity", position="fill", color = "black",linewidth = 0.5) + 
  ylab("Relative Abundance") +
  scale_y_continuous(expand = c(0,0)) + #remove the space below the 0 of the y axis in the graph
  theme_bw() +
  scale_fill_manual(values= rev(c("dodgerblue4",
                                  "darkolivegreen",
                                  "darkred",
                                  "orange",
                                  "magenta",
                                  "yellow",
                                  "gray")))+
  theme(axis.line = element_line(size = 0), 
        panel.background = element_blank(), 
        panel.grid.major = element_blank(),  #remove major-grid labels
        panel.grid.minor = element_blank(), 
        axis.title = element_text(family = "serif",size = 18), 
        axis.text = element_text(size = 14), 
        axis.text.x = element_text(size = 12, family = "serif", angle = 45,hjust = 1), 
        axis.text.y = element_text(family = "serif", size = 14),
        axis.title.x =  element_text(size = 0),
        legend.text = element_text(size = 15,  family = "serif"), 
        legend.title = element_text(size = 18, family = "serif"))+
  guides(fill=guide_legend(ncol =1))+
  labs(fill = "Microbial Phylum")
Fig_phylum

pdf(file = paste0(dir_graph,"compo_Phylum.pdf"), he = 7 , wi = 14)
Fig_phylum
dev.off()

Family composition

physeq <- read.table(file = paste0(dir_dataset, "physeq.txt"), sep ="\t", header = T)
compo_family <- physeq %>%
  group_by(Phylum, Family) %>%
  summarise(Prop_family = sum(Abundance)) %>%
  arrange(desc(Prop_family)) # arrange by prop in order to select then the most abundant

physeq$Family <- factor(physeq$Family, levels = rev(as.character(compo_family$Family)))
rev(levels(physeq$Family))[1:20] # Here, we select the 20 most abundant families
physeq$Family <- fct_other(physeq$Family, keep = rev(levels(physeq$Family))[1:20])

# Select family colors depending on the belonging phylum
counts <- compo_family[1:20,] %>% count(Phylum)
fam_col <- cbind(compo_family[1:20,] %>% 
                   arrange(Phylum, Prop_family), 
                 "Colors" = c("bisque3","orange", #for actinobacteria
                              "darkred","darkmagenta" ,"firebrick", # for bacteroidetes
                              "green","darkgreen", "darkolivegreen1","darkolivegreen" , # for firmicutes
                              "darkslategray4","darkslategray3","cadetblue1","darkturquoise","turquoise", #for proteo, melt the gradient
                              "cyan","cornflowerblue","dodgerblue", "blue","darkblue","dodgerblue4")) 

barplot <- physeq %>%
  group_by(ID, Family) %>%
  summarise(Prop_family = sum(Abundance)) 
barplot <- left_join(barplot, fam_col %>% as.data.frame() %>% select(Family,Colors), by = "Family")
barplot$Colors <- fct_explicit_na(barplot$Colors, na_level = "dimgray")
barplot$Family <- factor(barplot$Family, levels = c(fam_col$Family, "Other"))
barplot$Colors <- factor(barplot$Colors, levels = c(fam_col$Colors, "dimgray"))
barplot$ID <- factor(barplot$ID, levels = unique(arrange(physeq, physeq$Total)$ID))

Fig_fam <- barplot %>%
  ggplot(aes(x = ID, y = Prop_family, fill = Family)) + 
  geom_bar(stat="identity", position="fill",color = "black", linewidth = 0.5) + 
  ylab("Relative Abundance") +
  scale_y_continuous(expand = c(0,0)) + #remove the space below the 0 of the y axis in the graph
  theme_bw() +
  scale_fill_manual(values= levels(barplot$Colors))+
  theme(axis.line = element_line(size = 0), 
        panel.background = element_blank(), 
        panel.grid.major = element_blank(),  #remove major-grid labels
        panel.grid.minor = element_blank(), 
        axis.title = element_text(family = "serif",size = 18), 
        axis.text = element_text(size = 14), 
        axis.text.x = element_text(size = 12, family = "serif", angle = 45,hjust = 1), 
        axis.text.y = element_text(family = "serif", size = 14),
        axis.title.x =  element_text(size = 0),
        legend.text = element_text(size = 15,  family = "serif"), 
        legend.title = element_text(size = 18, family = "serif"))+
  guides(fill=guide_legend(ncol =1))+
  labs(fill = "Microbial Family")
Fig_fam

pdf(file = paste0(dir_graph, "Fig_family.pdf"), he = 7 , wi = 14)
Fig_fam
dev.off()

LEfSe analysis

We want to determine if it does exist discriminant microbial taxa that can be considered as candidate to be manipulative microorganisms. In that way, we run a Linear Effect Size discriminant analysis in order to discriminate taxa that are statistically more prevalent and abundant in an aggregation status

load(paste0(dir_dataset, "ps_rff.RData"))
samp_data <- ps_rff %>% sample_data() %>% as.data.frame()

otu_asv <- cbind(rownames(ps_rff %>% otu_table() %>% as.data.frame() %>% t()),
                 ps_rff %>% otu_table() %>% as.data.frame() %>% t())
colnames(otu_asv) <- c("Status", samp_data$status)
write.table(otu_asv, file = paste0(dir_dataset, "lefse_tab_asv.txt"), sep = "\t", row.names = F)

Run the LEfSe in the Galaxy server. Download the output of the analysis in the “dir_dataset” and call it “LDA_Effect_Size_ASV”

LDA_Effect_Size_ASV <- read.delim(paste0(dir_dataset, "LDA_Effect_Size_ASV"), header=FALSE)

LDA_result <- cbind(LDA_Effect_Size_ASV, "Clustering" = rep("100%")) %>%
  filter(V3 %in% c("High", "Low"))
colnames(LDA_result) <- c("ASV","LDA_res" , "Discriminant" , "LDA_res2" , "p-value", "Clustering")
LDA_result <- left_join(LDA_result, ps_rff %>% subset_taxa(ASV %in% LDA_result$ASV) %>% tax_table() %>% as.data.frame(), by = "ASV")
LDA_result$Taxonomy <- paste(LDA_result$ASV, LDA_result$Genus, LDA_result$Class, sep = "|")
write.table(LDA_result, file=paste0(dir_dataset, "LDA_result.txt"), sep="\t", row.names=F, col.names=T)


tab_asv <- left_join(
  left_join(  
    cbind("Status" = colnames(otu_asv), 
          otu_asv %>% 
            t() %>%
            data.frame() %>%
            select(LDA_result$ASV) %>%
            mutate_if(is.character, as.numeric)) %>%
      pivot_longer(!Status, names_to = "ASV", values_to = "Abundance") %>%
      group_by(ASV, Status) %>%
      summarise(Abundance = sum(Abundance)), 
    cbind("Status" = colnames(otu_asv), 
          otu_asv %>% 
            t() %>%
            data.frame() %>%
            select(LDA_result$ASV) %>%
            mutate_if(is.character, as.numeric)) %>%
      pivot_longer(!Status, names_to = "ASV", values_to = "Abundance") %>%
      filter(Abundance != 0) %>%
      count(ASV, Status, name = "Occurrence"),
    by = c("ASV", "Status")),
  LDA_result, 
  by = "ASV")

write.table(tab_asv, file = paste0(dir_dataset, "tab_asv.txt"), sep ="\t", row.names = F)
tab_asv <- read.table(paste0(dir_dataset, "tab_asv.txt"), sep ="\t", header = T)

tab_asv$Status <- str_replace_all(tab_asv$Status, c("Low" = "Low-aggregation",
                                                    "High" = "High-aggregation"))
tab_asv$Occurrence <- replace_na(tab_asv$Occurrence, 0)
tab_asv[tab_asv$Status == "Low-aggregation",]$Occurrence <- tab_asv[tab_asv$Status == "Low-aggregation",]$Occurrence*-1
tab_asv <- tab_asv %>% arrange(desc(Discriminant))
tab_asv$Taxonomy <- factor(tab_asv$Taxonomy, levels = tab_asv$Taxonomy %>% unique())
tab_asv$Discriminant <- factor(tab_asv$Discriminant, levels = c("Low", "High"))
tab_asv$Status <- factor(tab_asv$Status, levels = c("Low-aggregation", "High-aggregation"))

ggplot(tab_asv,aes(y = Taxonomy, x = Occurrence, fill = Status ,alpha = log(Abundance))) +
  geom_bar(stat = "identity", position = "identity",color = "black",linewidth = 0.3) +
  scale_x_continuous(limits = c(-20,19))+
  xlab("Number of females hosting the discriminant")+
  scale_fill_manual(values = c("darkblue", "darkred"))+
  theme_bw() +
  theme(axis.line = element_line(size = 0), 
        panel.background = element_blank(), 
        panel.grid.major = element_blank(),  #remove major-grid labels
        panel.grid.minor = element_blank(), 
        axis.title = element_text(family = "serif",size = 16), 
        axis.text = element_text(size = 16), 
        axis.text.x = element_text(size = 16, family = "serif"), 
        axis.text.y = element_text(family = "serif", size = 16),
        axis.title.x =  element_text(size = 16),
        axis.title.y =  element_text(size = 0),
        legend.text = element_text(size = 16,  family = "serif"), 
        legend.title = element_text(size = 18, family = "serif"))+
  guides(fill=guide_legend(ncol =1))+
  labs(fill = "Aggregation level", 
       alpha = "Abundance (log)", 
       title = "ASV Discriminants")

It exists 10 ASVs discriminants but they are actually in very low occurrence in the samples with strong variation in their abundance. Indeed, it does not exist any exclusive that are present in all less- or high-aggregation females.

Diversity calculs

We then calculated the alpha diversity and beta-diversity of the gut microbiota of the 39 tested females.

Alpha diversity

Regarding the alpha diversity, we used the direct observed richness as a proxy for the qualitative taxonomic richness and the Shannon entropy for its quantitative equivalent. Their respective equivalents that include the phylogenetic relatedness between microbial sequences are the Faith and the Hill1 (q = 1) indices (Chao et al., 2010).

load(file = paste0(dir_dataset ,"ps_rff.RData"))

alpha_data <- left_join(
  cbind("ID" = sample_names(ps_rff),
        "Observed.richness" = pd(samp= ps_rff@otu_table , tree = ps_rff@phy_tree , include.root =  T)$SR,
        estimate_richness(ps_rff , measures = c("Shannon")),
        "Faith" = pd(samp= ps_rff@otu_table , tree = ps_rff@phy_tree , include.root =  T)$PD,
        "Hill1" = hill_phylo(comm = ps_rff@otu_table, tree = ps_rff@phy_tree, q = 1)) %>%
    as.data.frame(),
  ps_rff %>% sample_data() %>% as.data.frame(),
  by = "ID")

write.table(alpha_data, file = paste0(dir_dataset, "alpha_data.txt"), sep = "\t", row.names = F)

Beta diversity

Preparation of the matrix and the taxonomy table to calcul the distances between sample pairs.

load(paste0(dir_dataset, "ps_rff.RData"))
samp_data <- ps_rff %>% sample_data() %>% as.data.frame()
samp_data$status <- str_replace_all(samp_data$status , 
                                    c("Low" = "Low-aggregation",
                                      "High" = "High-aggregation"))
samp_data$status <- factor(samp_data$status, levels = c("Low-aggregation", "High-aggregation"))
otu_table <- ps_rff@otu_table %>% as.data.frame()
tax_table <- ps_rff@tax_table %>% as.data.frame()
write.table(otu_table, file = paste0(dir_dataset, "otu_table.txt"), sep = "\t", row.names = T, col.names = T)  
write.table(tax_table, file = paste0(dir_dataset, "tax_table.txt"), sep = "\t", row.names = T, col.names = T)

For beta-diversity, we used a taxonomic qualitative distance with the Jaccard metric that is used on a binary matrix with ASVs presence/absence by sample while its quantitative equivalent Bray-Curtis is calculated on the relative abundances of ASVs in each sample. We also used their phylogenetic equivalents with the Unifrac metrics for qualitative (Unweighted) and quantitative (Weighted) distances (Lozupone and Knight, 2005,Yang et al., 2021).

otu.jacc <- vegdist(otu_table, method = "jaccard")
otu.bc <- vegdist(otu_table, method = "bray")
otu.uu <- UniFrac(ps_rff, weighted = F, normalized=F, parallel = F, fast=T)
otu.wu <- UniFrac(ps_rff, weighted = T, normalized=F, parallel = F, fast=T)

To analyse the variability in microbial assemblies between low- and high-aggregation hosts, we assessed the dispersion for all metrics (i.e., distance of each sample from the centroïd among the low and high-aggregation levels) using the betadisper function from the vegan package (Oksanen et al., 2022).

disp.jacc <- betadisper(otu.jacc, samp_data$status)
sig.jacc <- permutest(disp.jacc)
Distances.jacc <- disp.jacc$distances
disp.bc <- betadisper(otu.bc, samp_data$status)
sig.bc <- permutest(disp.bc)
Distances.bc <- disp.bc$distances
disp.uu <- betadisper(otu.uu, samp_data$status)
Distances.uu <- disp.uu$distances
sig.uu <- permutest(disp.uu)
disp.wu <- betadisper(otu.wu, samp_data$status)
sig.wu <- permutest(disp.wu)
Distances.wu <- disp.wu$distances

alpha_data <- read.table(file = paste0(dir_dataset, "alpha_data.txt"), sep = "\t", header = T, allowEscapes = )

micro_div_data <- cbind(alpha_data %>% select(ID:Hill1),
                       "Jacc.dispersion" = Distances.jacc,
                       "BC.dispersion" = Distances.bc,
                       "UU.dispersion" = Distances.uu,
                       "WU.dispersion" = Distances.wu, 
                       alpha_data %>% select(sampling_date:weight))

write.table(micro_div_data, file = paste0(dir_dataset, "micro_div_data.txt"), sep ="\t", row.names = F)

micro_div_data_longer <- micro_div_data %>% 
  pivot_longer(cols = Observed.richness:WU.dispersion, names_to = "Variable", values_to = "Value") 
write.table(micro_div_data_longer, file = paste0(dir_dataset, "micro_div_data_longer.txt"), sep ="\t", row.names = F)

Statistical tests

T-test

We used these metrics in eight t-tests and four PERMANOVAs to compare the alpha diversity, microbial variability, and microbial composition between females with high or low AS.

Before performing t-tests, check the variance homogeneity.

micro_div_data <- read.table(file = paste0(dir_dataset, "micro_div_data.txt"), sep ="\t",header =T )
# Alpha indices
paste0("Homogeneity of richness between status p-value = ", round(var.test(Observed.richness~ status,micro_div_data)$p.value, 2))
## [1] "Homogeneity of richness between status p-value = 0.72"
paste0("Homogeneity of Shannon between status p-value = ", round(var.test(Shannon~status,micro_div_data)$p.value, 2))
## [1] "Homogeneity of Shannon between status p-value = 0.7"
paste0("Homogeneity of Faith between status p-value = ", round(var.test(Faith~status,micro_div_data)$p.value, 2))
## [1] "Homogeneity of Faith between status p-value = 0.98"
paste0("Homogeneity of Hill1 between status p-value = ", round(var.test(Hill1~status,micro_div_data)$p.value, 2))
## [1] "Homogeneity of Hill1 between status p-value = 0.72"
# Beta indices
paste0("Homogeneity of Jacc dispersion between status p-value = ", round(var.test(Jacc.dispersion~ status,micro_div_data)$p.value, 2))
## [1] "Homogeneity of Jacc dispersion between status p-value = 0.68"
paste0("Homogeneity of BC dispersion between status p-value = ", round(var.test(BC.dispersion~ status,micro_div_data)$p.value, 2))
## [1] "Homogeneity of BC dispersion between status p-value = 0.91"
paste0("Homogeneity of UU dispersion between status p-value = ", round(var.test(UU.dispersion~ status,micro_div_data)$p.value, 2))
## [1] "Homogeneity of UU dispersion between status p-value = 0.08"
paste0("Homogeneity of WU dispersion between status p-value = ", round(var.test(WU.dispersion~ status,micro_div_data)$p.value, 2))
## [1] "Homogeneity of WU dispersion between status p-value = 0"

and proceed to t-test to compare the alpha and beta between levels of aggregation

micro_div_data <- read.table(file = paste0(dir_dataset, "micro_div_data.txt"), sep ="\t",header =T )
# Alpha t test
paste0("Variance comparisions of richness between status: t= ",round(t.test(Observed.richness~status,micro_div_data, var=T)$statistic,2), "; df= ", t.test(Observed.richness~status,micro_div_data, var=T)$parameter,"; p-value= ",round(t.test(Observed.richness~status,micro_div_data, var=T)$p.value))
## [1] "Variance comparisions of richness between status: t= -1.44; df= 37; p-value= 0"
paste0("Variance comparisions of Shannon between status: t= ",round(t.test(Shannon~status,micro_div_data, var=T)$statistic,2), "; df= ", t.test(Shannon~status,micro_div_data, var=T)$parameter,"; p-value= ",round(t.test(Shannon~status,micro_div_data, var=T)$p.value))
## [1] "Variance comparisions of Shannon between status: t= -1.05; df= 37; p-value= 0"
paste0("Variance comparisions of Faith between status: t= ",round(t.test(Faith~status,micro_div_data, var=T)$statistic,2), "; df= ", t.test(Faith~status,micro_div_data, var=T)$parameter,"; p-value= ",round(t.test(Faith~status,micro_div_data, var=T)$p.value))
## [1] "Variance comparisions of Faith between status: t= -0.87; df= 37; p-value= 0"
paste0("Variance comparisions of Hill1 between status: t= ",round(t.test(Hill1~status,micro_div_data, var=T)$statistic,2), "; df= ", t.test(Hill1~status,micro_div_data, var=T)$parameter,"; p-value= ",round(t.test(Hill1~status,micro_div_data, var=T)$p.value))
## [1] "Variance comparisions of Hill1 between status: t= -0.18; df= 37; p-value= 1"
# Beta divesity
paste0("Variance comparisions of Jacc.dispersion between status: t= ",round(t.test(Jacc.dispersion~status,micro_div_data, var=T)$statistic,2), "; df= ", t.test(Jacc.dispersion~status,micro_div_data, var=T)$parameter,"; p-value= ",round(t.test(Jacc.dispersion~status,micro_div_data, var=T)$p.value))
## [1] "Variance comparisions of Jacc.dispersion between status: t= 1.37; df= 37; p-value= 0"
paste0("Variance comparisions of BC.dispersion between status: t= ",round(t.test(BC.dispersion~status,micro_div_data, var=T)$statistic,2), "; df= ", t.test(BC.dispersion~status,micro_div_data, var=T)$parameter,"; p-value= ",round(t.test(BC.dispersion~status,micro_div_data, var=T)$p.value))
## [1] "Variance comparisions of BC.dispersion between status: t= 1.23; df= 37; p-value= 0"
paste0("Variance comparisions of UU.dispersion between status: t= ",round(t.test(UU.dispersion~status,micro_div_data, var=T)$statistic,2), "; df= ", t.test(UU.dispersion~status,micro_div_data, var=T)$parameter,"; p-value= ",round(t.test(UU.dispersion~status,micro_div_data, var=T)$p.value))
## [1] "Variance comparisions of UU.dispersion between status: t= 0.78; df= 37; p-value= 0"
paste0("Variance comparisions of WU.dispersion between status: = ",round(t.test(WU.dispersion~status,micro_div_data, var=F)$statistic,2), "; df= ", t.test(WU.dispersion~status,micro_div_data, var=F)$parameter,"; p-value= ",round(t.test(WU.dispersion~status,micro_div_data, var=F)$p.value)) # welch test because variances were not homogeneous
## [1] "Variance comparisions of WU.dispersion between status: = 0.5; df= 24.8930893770938; p-value= 1"
micro_div_data_longer <- read.table(file = paste0(dir_dataset, "micro_div_data_longer.txt"), sep ="\t", header = T)
micro_div_data_longer$Variable <- factor(micro_div_data_longer$Variable, levels = c("Observed.richness",
                                                                            "Shannon",
                                                                            "Faith",
                                                                            "Hill1",
                                                                            "Jacc.dispersion",
                                                                            "BC.dispersion",
                                                                            "UU.dispersion",
                                                                            "WU.dispersion"))
micro_div_data_longer$status <- factor(micro_div_data_longer$status , levels = c("Low", "High"))  

Fig_boxplot <- micro_div_data_longer %>%
  ggplot(aes(x = status , y = Value, color = status)) +
  geom_jitter(position = position_jitter(width = .20), alpha = 0.5, size = 3) + 
  scale_color_manual(values = c("darkblue" , "darkred")) +
  theme_bw() +
  geom_boxplot(alpha=0.1, outlier.colour = NA) + 
  theme(axis.title.x = element_blank(),
        axis.title.y = element_blank(),
        axis.text.y = element_text(size=18, family = "serif"),
        axis.text = element_text(family = "serif",size = 14),
        axis.text.x = element_text(family = "serif",size = 18, angle = 0),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        strip.text.x = element_text(size=14, family = "serif"),
        legend.position="none") + 
  facet_wrap( ~ Variable, nrow=2, ncol = 4, scales = "free") +
  geom_signif(comparisons = list(c("Low", "High")), 
              map_signif_level = T, textsize=4, color="black", family = "serif", vjust = 0)
Fig_boxplot 

pdf(file = paste0(dir_graph, "fig_boxplot.pdf"), he = 9, wi = 12)
Fig_boxplot
dev.off()

PERMANOVAs and PCoA

Finally, we used four PERMANOVAs to test the influence of the aggregation status on the microbial composition

paste0("PERMANOVA on Jaccard distance R2= ", round(adonis2(otu.jacc ~ samp_data$status)$R2[1],2) , "; p-value= ",round(adonis2(otu.jacc ~ samp_data$status)$`Pr(>F)`[1],2))
## [1] "PERMANOVA on Jaccard distance R2= 0.02; p-value= 0.86"
paste0("PERMANOVA on BC distance R2= ", round(adonis2(otu.bc ~ samp_data$status)$R2[1],2) , "; p-value= ",round(adonis2(otu.bc ~ samp_data$status)$`Pr(>F)`[1],2))
## [1] "PERMANOVA on BC distance R2= 0.02; p-value= 0.85"
paste0("PERMANOVA on UU distance R2= ", round(adonis2(otu.uu ~ samp_data$status)$R2[1],2) , "; p-value= ",round(adonis2(otu.uu ~ samp_data$status)$`Pr(>F)`[1],2))
## [1] "PERMANOVA on UU distance R2= 0.03; p-value= 0.16"
paste0("PERMANOVA on WU distance R2= ", round(adonis2(otu.wu ~ samp_data$status)$R2[1],2) , "; p-value= ",round(adonis2(otu.wu ~ samp_data$status)$`Pr(>F)`[1],2))
## [1] "PERMANOVA on WU distance R2= 0.02; p-value= 0.66"
#  *Jaccard 
pcoa.sub.jacc <- pcoa(otu.jacc)
pcoa_coord.jacc <- pcoa.sub.jacc$vectors[,1:3]
hull.jacc <- cbind(pcoa_coord.jacc, samp_data) # Contruction of the table for graphic 

pcoa.jacc <- ggplot() +
  geom_point(data = hull.jacc, aes(x=Axis.1, y=Axis.2, size = status, color = status), alpha = 0.7, shape = 16) +
  geom_hline(yintercept=0, colour="lightgrey", linetype = 2) +
  geom_vline(xintercept=0, colour="lightgrey", linetype = 2) +
  xlab(paste("PCo1 (", round(pcoa.sub.jacc$values$Relative_eig[1]*100, 1), "%)")) +
  ylab(paste("PCo2 (", round(pcoa.sub.jacc$values$Relative_eig[2]*100, 1), "%)"))  +
  theme_bw() +
  scale_color_manual(values = c("darkblue", "darkred"))+
  coord_equal() +
  theme(axis.title.x = element_text(size=14, family = "serif"), # remove x-axis labels
        axis.title.y = element_text(size=14, family = "serif"), # remove y-axis labels
        axis.text.x = element_text(size=14, family = "serif"),
        axis.text.y = element_text(size=14, family = "serif"),
        panel.background = element_blank(),
        panel.grid.major = element_blank(),  #remove major-grid labels
        panel.grid.minor = element_blank(),  #remove minor-grid labels
        plot.background = element_blank(),
        axis.title = element_text(family = "serif", size = 22), 
        legend.text = element_text(size = 0, family = "serif"),
        legend.title = element_text(size = 0,family = "serif"))+
  labs(title = "PCoA on Jaccard distances", 
       size = "Status", color = "Status")

# * Bray-Curtis 
pcoa.sub.bc <- pcoa(otu.bc)
pcoa_coord.bc <- pcoa.sub.bc$vectors[,1:3]
hull.bc <- cbind(pcoa_coord.bc, samp_data) # Contruction of the table for graphic 

pcoa.bc <- ggplot() +
  geom_point(data = hull.bc, aes(x=Axis.1, y=Axis.2, size = status, color = status), alpha = 0.7, shape = 16) +
  geom_hline(yintercept=0, colour="lightgrey", linetype = 2) +
  geom_vline(xintercept=0, colour="lightgrey", linetype = 2) +
  xlab(paste("PCo1 (", round(pcoa.sub.bc$values$Relative_eig[1]*100, 1), "%)")) +
  ylab(paste("PCo2 (", round(pcoa.sub.bc$values$Relative_eig[2]*100, 1), "%)"))  +
  theme_bw() +
  scale_color_manual(values = c("darkblue", "darkred"))+
  coord_equal() +
  theme(axis.title.x = element_text(size=14, family = "serif"), # remove x-axis labels
        axis.title.y = element_text(size=14, family = "serif"), # remove y-axis labels
        axis.text.x = element_text(size=14, family = "serif"),
        axis.text.y = element_text(size=14, family = "serif"),
        panel.background = element_blank(),
        panel.grid.major = element_blank(),  #remove major-grid labels
        panel.grid.minor = element_blank(),  #remove minor-grid labels
        plot.background = element_blank(),
        axis.title = element_text(family = "serif", size = 22), 
        legend.text = element_text(size = 0, family = "serif"),
        legend.title = element_text(size = 0,family = "serif"))+
  labs(title = "PCoA on Bray-Curtis distances", 
       size = "Status", color = "Status")

# * Unweighted Unifrac
pcoa.sub.uu <- pcoa(otu.uu)
pcoa_coord.uu <- pcoa.sub.uu$vectors[,1:3]
hull.uu <- cbind(pcoa_coord.uu, samp_data)

pcoa.uu <- ggplot() +
  geom_point(data = hull.uu, aes(x=Axis.1, y=Axis.2, size = status, color = status), alpha = 0.7, shape = 16) +
  geom_hline(yintercept=0, colour="lightgrey", linetype = 2) +
  geom_vline(xintercept=0, colour="lightgrey", linetype = 2) +
  xlab(paste("PCo1 (", round(pcoa.sub.uu$values$Relative_eig[1]*100, 1), "%)")) +
  ylab(paste("PCo2 (", round(pcoa.sub.uu$values$Relative_eig[2]*100, 1), "%)"))  +
  theme_bw() +
  scale_color_manual(values = c("darkblue", "darkred"))+
  coord_equal() +
  theme(axis.title.x = element_text(size=14, family = "serif"), # remove x-axis labels
        axis.title.y = element_text(size=14, family = "serif"), # remove y-axis labels
        axis.text.x = element_text(size=14, family = "serif"),
        axis.text.y = element_text(size=14, family = "serif"),
        panel.background = element_blank(),
        panel.grid.major = element_blank(),  #remove major-grid labels
        panel.grid.minor = element_blank(),  #remove minor-grid labels
        plot.background = element_blank(),
        axis.title = element_text(family = "serif", size = 22), 
        legend.text = element_text(size = 0, family = "serif"),
        legend.title = element_text(size = 0,family = "serif"))+
  labs(title = "PCoA on Unweighted Unifrac distances", 
       size = "Status", color = "Status")


# * Weighted Unifrac 
pcoa.sub.wu <- pcoa(otu.wu)
pcoa_coord.wu <- pcoa.sub.wu$vectors[,1:3]
hull.wu <- cbind(pcoa_coord.wu, samp_data)

pcoa.wu <- ggplot() +
  geom_point(data = hull.wu, aes(x=Axis.1, y=Axis.2, size = status, color = status), alpha = 0.7, shape = 16) +
  geom_hline(yintercept=0, colour="lightgrey", linetype = 2) +
  geom_vline(xintercept=0, colour="lightgrey", linetype = 2) +
  xlab(paste("PCo1 (", round(pcoa.sub.wu$values$Relative_eig[1]*100, 1), "%)")) +
  ylab(paste("PCo2 (", round(pcoa.sub.wu$values$Relative_eig[2]*100, 1), "%)"))  +
  theme_bw() +
  scale_color_manual(values = c("darkblue", "darkred"))+
  coord_equal() +
  theme(axis.title.x = element_text(size=14, family = "serif"), # remove x-axis labels
        axis.title.y = element_text(size=14, family = "serif"), # remove y-axis labels
        axis.text.x = element_text(size=14, family = "serif"),
        axis.text.y = element_text(size=14, family = "serif"),
        panel.background = element_blank(),
        panel.grid.major = element_blank(),  #remove major-grid labels
        panel.grid.minor = element_blank(),  #remove minor-grid labels
        plot.background = element_blank(),
        axis.title = element_text(family = "serif", size = 22), 
        legend.text = element_text(size = 0, family = "serif"),
        legend.title = element_text(size = 0,family = "serif"))+
  labs(title = "PCoA on Weighted Unifrac distances", 
       size = "Status", color = "Status")

legend <- ggplot() +
  geom_point(data = hull.wu, aes(x=Axis.1, y=Axis.2, size = status, color = status), alpha = 0.7, shape = 16) +
  geom_hline(yintercept=0, colour="lightgrey", linetype = 2) +
  geom_vline(xintercept=0, colour="lightgrey", linetype = 2) +
  xlab(paste("PCo1 (", round(pcoa.sub.wu$values$Relative_eig[1]*100, 1), "%)")) +
  ylab(paste("PCo2 (", round(pcoa.sub.wu$values$Relative_eig[2]*100, 1), "%)"))  +
  theme_bw() +
  scale_color_manual(values = c("darkblue", "darkred"))+
  coord_equal() +
  theme(axis.title.x = element_text(size=12, family = "serif"), # remove x-axis labels
        axis.title.y = element_text(size=12, family = "serif"), # remove y-axis labels
        axis.text.x = element_text(size=12, family = "serif"),
        axis.text.y = element_text(size=12, family = "serif"),
        panel.background = element_blank(),
        panel.grid.major = element_blank(),  #remove major-grid labels
        panel.grid.minor = element_blank(),  #remove minor-grid labels
        plot.background = element_blank(),
        axis.title = element_text(family = "serif", size = 22), 
        legend.text = element_text(size = 16, family = "serif"),
        legend.title = element_text(size = 16,family = "serif"))+
  labs(title = "PCoA on Weighted Unifrac distances", 
       size = "Status", color = "Status")

ggarrange(legend.grob = get_legend(legend),
          legend = "right",
          pcoa.jacc,pcoa.bc,
          pcoa.wu,pcoa.uu, ncol = 2, nrow = 2)

pdf(file = paste0(dir_graph, "PCoA.pdf"), he = 12, wi = 12)
ggarrange(legend.grob = get_legend(legend),
          legend = "right",
          pcoa.jacc,pcoa.bc,
          pcoa.wu,pcoa.uu, ncol = 2, nrow = 2)
dev.off()

Part IV: Gut transplantation

back to top

Lastly, download the data of samples involved in the gut transplant with their AS before transplant and after transplant, with their respective combinations

data_recipient <- read.table(file = paste0(dir_dataset, "aggregation_after_transplant.txt"), sep = "\t", header = T)
data_transplant <- read.table(file = paste0(dir_dataset, "transplant_data.txt"), sep = "\t", header = T)

As made for the AS_data file we create AS_transplant with the AS72 after gut transplant and join it to the previous information concerning the gut donor and gut recipient with their respective aggregation level.

data_transplant <- read.table(file = paste0(dir_dataset, "transplant_data.txt"), sep = "\t", header = T)
AS_transplant <- left_join(data_transplant,
                           data_recipient %>% 
                             group_by(ID_Recipient,day) %>% 
                             summarise(Aggregation = sum(attraction)) %>%
                             pivot_wider(names_from = day, values_from = Aggregation) %>%
                             mutate(AS72_after = sum(c_across(D1_after:D3_after))) %>%
                             as.data.frame(),
                           by = "ID_Recipient") %>%
  mutate(Delta = AS72_after-AS72_before) # Create the delta variation of the AS

write.table(AS_transplant , file = paste0(dir_dataset, "AS_transplant.txt"), sep = "\t", row.names = F)

Finally, we analysed data from the transplant experiment using a LM, in which we entered the AS measured after gut transplant as a response variable, while we used the aggregation status (i.e., high or low AS) of the recipient female before gut transplant, the aggregation status of the donor female before transplant and their interaction as explanatory variables.

AS_transplant <- read.table(file = paste0(dir_dataset, "AS_transplant.txt"), sep = "\t",header=T)
AS_transplant$Couple <- factor(AS_transplant$Couple, levels = c("Low_Low",
                                                                "High_Low"))

lm_transplant <- lm(AS72_after ~  AS72_Donor, 
                  data = AS_transplant)
simulationOutput <- simulateResiduals(fittedModel = lm_transplant, plot = T)

testResiduals(lm_transplant)

car::Anova(lm_transplant)

t.test(Delta~ Couple,
       AS_transplant %>% filter(Couple %in% c("Low_Low", "High_Low")), 
       var=T)
AS_transplant <- read.table(file = paste0(dir_dataset, "AS_transplant.txt"), sep = "\t",header=T)
AS_transplant$Couple <- factor(AS_transplant$Couple, levels = c("Low_Low",
                                                                "High_Low"))
colnames(AS_transplant)[c(13,17)] <-colnames(AS_transplant)[c(13,17)] <- c("Before", "After")


Fig_transplant <- ggpaired(AS_transplant %>% filter(Couple %in% c("Low_Low", "High_Low")), 
         cond1 = "Before", cond2 = "After",
         line.color = "gray", 
         line.size = 0.4,
         point.size = 0,
         fill = "condition",
         palette = c("aliceblue","azure3")) +
  geom_point(alpha = 0.5, size = 3) +
  theme(axis.title.x = element_blank(),
        axis.title.y = element_text(family = "serif",size = 16),
        axis.text = element_text(family = "serif",size = 16),
        axis.text.x = element_text(family = "serif",size = 16),
        axis.text.y = element_text(family = "serif",size = 16),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        legend.position = "none",
        strip.text.x = element_text(size=15, family = "serif"))+
  facet_grid(~ Couple)+
  geom_vline(xintercept = 2.59, linetype="dotted", color = "black", size=.5)+
  labs(y = "Aggregation Score 72h")
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
Fig_transplant

pdf(file = paste0(dir_graph, "Fig_transplant.pdf"), he = 7, wi = 10)
Fig_transplant
dev.off()
LS0tCnRpdGxlOiAiR3V0IG1pY3JvYmlvdGEgZG9lcyBub3QgZHJpdmUgaG9zdCBzb2NpYWxpdHkgaW4gdGhlIEV1cm9wZWFuIGVhcndpZyIKc3VidGl0bGUgOiBEYXRhIHByb2Nlc3NlcyAmIGFuYWx5c2VzCmF1dGhvcjogfAogIHwgTWFyaWUtQ2hhcmxvdHRlLCBDaGV1dGluXmEsMV4sIEJlbmphbWluLCBMZWNsZXJjXmFeLCBKb8OrbCwgTWV1bmllcl5hXi4gIAogIDxwPiA8L3A+IAogIDxmb250IHNpemU9IjIiPiBeYV4gSW5zdGl0dXQgZGUgUmVjaGVyY2hlIHN1ciBsYSBCaW9sb2dpZSBkZSBs4oCZSW5zZWN0ZSwgVU1SIDcyNjEsIENOUlMsIFVuaXZlcnNpdHkgb2YgVG91cnMsIFRvdXJzLCBGcmFuY2U8L2ZvbnQ+ICAKICA8cD4gPC9wPiAKICA8Zm9udCBzaXplPSIzIj4gXjFeVG8gd2hvbSBjb3JyZXNwb25kZW5jZSBzaG91bGQgYmUgYWRkcmVzc2VkLiBFLW1haWw6IG1jY2hldXRpbkBnbWFpbC5jb208L2ZvbnQ+ICAKYWJzdHJhY3Q6ICJBIHF1aWNrIHdvcmtmbG93IHRvIHBlcmZvcm1lZCB0aGUgYW5hbHlzZXMgdXNlZCB0byBpbnZlc3RpZ2F0ZSB0aGUgaW5kaXZpZHVhbCBhZ2dyZWdhdGlvbiB2YXJpYXRpb24gaW4gdGhlIEV1cm9wZWFuIGVhcndpZyBhZHVsdCBmZW1hbGUgKkZvcmZpY3VsYSBhdXJpY3VsYXJpYSouIEhlcmUgd2UgcmVwb3J0IGJvdGggbWV0aG9kcyBhbmQgcmVzdWx0cyBpbiBwbGFpbiB0ZXh0IGFuZCBSIGNvZGUuIFlvdSBjYW4gY2hvb3NlIHRvIHNob3cgYWxsIGNvZGUgb3IgaGlkZSB0aGUgY29kZSB1c2luZyB0aGUgYnV0dG9uIGluIHRoZSB1cHBlciByaWdodCBoYW5kIGNvcm5lciBvZiB0aGUgZnJvbnQgcGFnZSAoZGVmYXVsdCBpcyBoaWRlKSBhcyB3ZWxsIGFzIGRvd25sb2FkIHRoZSAuUm1kIGZpbGUgdG8gdHdlYWsgdGhlIGNvZGUuIgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OiAKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgY29kZV9kb3dubG9hZDogeWVzCiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIGhpZ2hsaWdodDogZXNwcmVzc28KICAgIHRoZW1lOiBkYXJrbHkKICBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQKICAjY29vbF90aGVtZXM6IGRlZmF1bHQsIGNlcnVsZWFuLCBjb3NtbywgZGFya2x5LCBmbGF0bHksIGpvdXJuYWwsIGx1bWVuLCBzYW5kc3RvbmUsIHNpbXBsZXgsIHNwYWNlbGFiLCB1bml0ZWQsIHlldGkKLS0tCgoKVGhlIHBhY2thZ2UgbGlzdCB5b3UgbmVlZCB0byBpbnN0YWxsIGFuZCBsb2FkIGZvciB0aGUgd29ya2Zsb3c6IAoKYGBge3IgcGFja2FnZXMsIGV2YWwgPSBULCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpybShsaXN0PWxzKCkpCmNyYW5fcGFja2FnZXMgICA8LSBjKCJrbml0ciIsICJwaHlsb3NlcUdyYXBoVGVzdCIsICJwaHlsb3NlcSIsICJzaGlueSIsICJtaWNyb2Jpb21lIiwKICAgICAgICAgICAgICAgICAgICAgInRpZHl2ZXJzZSIsICJtaW5pVUkiLCAiY2FyZXQiLCAicGxzIiwgImUxMDcxIiwgImdncGxvdDIiLAogICAgICAgICAgICAgICAgICAgICAicmFuZG9tRm9yZXN0IiwiZW50cm9wYXJ0IiwgInZlZ2FuIiwgInBseXIiLCAiZHBseXIiLCJoZXJlIiwgImdncmVwZWwiLCAibmxtZSIsIAogICAgICAgICAgICAgICAgICAgICAiUi51dGlscyIsICJncmlkRXh0cmEiLCAiZ29vZ2xlZHJpdmUiLCAiZ29vZ2xlc2hlZXRzIiwgInBoYW5nb3JuIiwgImRldnRvb2xzIiwgCiAgICAgICAgICAgICAgICAgICAgICJybWFya2Rvd24iLCAic3lzIiwicGljYW50ZSIsInJlc2hhcGUyIiwgImRldnRvb2xzIiwgIlBNQSIsInN0cnVjdFNTSSIsImFkZTQiLCAKICAgICAgICAgICAgICAgICAgICAgImFwZSIsICJCaW9zdHJpbmdzIiwgImlncmFwaCIsICJnZ25ldHdvcmsiLCAiaW50ZXJncmFwaCIsICJpcHMiLCJzY2FsZXMiLCAia2FibGVFeHRyYSIsIAogICAgICAgICAgICAgICAgICAgICAicGdpcm1lc3MiLCJ0cmVlbWFwIiwgImdncHViciIsICJyc3RhdGl4IiwgImdndGhlbWVzIiwgImdncHViciIsIm91dGxpZXJzIiwgIklDQyIsCiAgICAgICAgICAgICAgICAgICAgICJtb21lbnRzIiwgImNvd3Bsb3QiLCAicGdpcm1lc3MiLCAiZHVubi50ZXN0IiwgInZpcmlkaXMiLCAiZ3JpZCIsICJBSUNjbW9kYXZnIiwgInJhc3RlciIsIAogICAgICAgICAgICAgICAgICAgICAiRmFjdG9NaW5lUiIsICJQZXJmb3JtYW5jZUFuYWx5dGljcyIsICJIbWlzYyIsICJESEFSTWEiLCAiYWRlNCIsICJjYXIiLCJzbXBsb3QiLAogICAgICAgICAgICAgICAgICAgICAibGFiZHN2IiwiZXBpdG9vbHMiLCJnZ3NpZ25pZiIsICJiaW9tZm9ybWF0IiwicmFuZG9tY29sb1IiLCAibHVicmlkYXRlIiwiZGljaHJvbWF0IiwKICAgICAgICAgICAgICAgICAgICAgImhpbGxSIiwgImFkZXNwYXRpYWwiLCAiRFQiKQoKZ2l0aHViX3BhY2thZ2VzIDwtIGMoImpmdWt1eWFtYS9waHlsb3NlcUdyYXBoVGVzdCIsJ3NtaW45NS9zbXBsb3QnLCAiZ2F1cmF2c2svcmFuYWNhcGEiLCAiamZxMy9Rc1J1dGlscyIsICJwbWFydGluZXphcmJpenUvcGFpcndpc2VBZG9uaXMvcGFpcndpc2VBZG9uaXMiKQpnaXRodWJfbGliIDwtIGMoInBoeWxvc2VxR3JhcGhUZXN0Iiwic21wbG90IiwgInJhbmFjYXBhIiwiUXNSdXRpbHMiLCAicGFpcndpc2VBZG9uaXMiKQpiaW9jX3BhY2thZ2VzICAgPC0gYygicGh5bG9zZXEiLCAiZ2VuZWZpbHRlciIsICJpbXB1dGUiLCAiZGFkYTIiLCAiREVDSVBIRVIiLCAiZ2d0cmVlIikKYGBgCgpgYGB7ciBzZXR1cCBpbnN0YWxsaW5nLCBldmFsID0gRiwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlPUZBTFNFLCBpbmNsdWRlID0gRkFMU0V9CiMgSW5zdGFsbCBDUkFOIHBhY2thZ2VzIChpZiBub3QgYWxyZWFkeSBpbnN0YWxsZWQpCmluc3QgPC0gY3Jhbl9wYWNrYWdlcyAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpCmlmIChhbnkoISBpbnN0KSkgewogIGluc3RhbGwucGFja2FnZXMoY3Jhbl9wYWNrYWdlc1shaW5zdF0sIHJlcG9zID0gImh0dHA6Ly9jcmFuLnJzdHVkaW8uY29tLyIpIH0KIwppbnN0IDwtIGdpdGh1Yl9wYWNrYWdlcyAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpCmlmIChhbnkoISBpbnN0KSkgewogIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YihnaXRodWJfcGFja2FnZXNbIWluc3RdLCBmb3JjZSA9IFQpIH0KIwppbnN0IDwtIGJpb2NfcGFja2FnZXMgJWluJSBpbnN0YWxsZWQucGFja2FnZXMoKQppZiAoYW55KCEgaW5zdCkpIHsKICBCaW9jTWFuYWdlcjo6aW5zdGFsbChiaW9jX3BhY2thZ2VzWyFpbnN0XSkgfQpgYGAKCmBgYHtyIHNldHVwIGxvYWRpbmcsIGV2YWwgPSBULCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2U9RkFMU0UscmVzdWx0cz0naGlkZSd9CmtuaXRyOjpvcHRzX2NodW5rJHNldChldmFsID0gVCkKIyBMb2FkIGxpYnJhcmllcwpzYXBwbHkoYyhjcmFuX3BhY2thZ2VzLCBiaW9jX3BhY2thZ2VzLCBnaXRodWJfbGliKSwgcmVxdWlyZSwgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKQpkZXRhY2goInBhY2thZ2U6ZHBseXIiLCB1bmxvYWQgPSBUUlVFKQpkZXRhY2goInBhY2thZ2U6cGx5ciIsIHVubG9hZCA9IFRSVUUpCmxpYnJhcnkocGx5cikKbGlicmFyeShkcGx5cikKc2V0LnNlZWQoMTAwMCkKYGBgCgpQcmVwYXJlIHlvdXIgd29ya2luZyBkaXJlY3RvcnkKCmBgYHtyIHNldF93ZCwgZXZhbD1UfQprbml0cjo6b3B0c19rbml0JHNldChyb290LmRpciA9IGdldHdkKCkpCnBhdGggPSBnZXR3ZCgpCiMgVGhpcyB3aWxsIHNldHdkIHRvIHdoZXJldmVyIHRoZSAuUm1kIGZpbGUgaXMgb3BlbmVkLgpkaXJfZGF0YXNldCA8LSBwYXN0ZTAocGF0aCwiL2Rpcl9kYXRhc2V0LyIpCmRpcl9ncmFwaCA8LSBwYXN0ZTAocGF0aCwiL2Rpcl9ncmFwaC8iKQpkaXJfZmFzdHFfc291cmNlIDwtIHBhc3RlMChkaXJfZGF0YXNldCwic2VxdWVuY2VzLyIpCmRpcl9yZWZkYiAgIDwtIHBhc3RlMChkaXJfZGF0YXNldCwgInJlZmVyZW5jZV9kYXRhYmFzZXMvIikKZGlyX2ZpbHRlcmVkIDwtIHBhc3RlMChkaXJfZGF0YXNldCwic2VxdWVuY2VzL2ZpbHRlcmVkLyIpCmBgYAoKYGBge3IgbWtkaXJzLCBldmFsPUZ9CmRpci5jcmVhdGUoZGlyX2RhdGFzZXQsIHJlY3Vyc2l2ZSA9IFQpOyBkaXIuY3JlYXRlKGRpcl9ncmFwaCwgcmVjdXJzaXZlID0gVCk7ZGlyLmNyZWF0ZShkaXJfZmlsdGVyZWQsIHJlY3Vyc2l2ZSA9IFRSVUUpIDsgZGlyLmNyZWF0ZShkaXJfcmVmZGIsIHJlY3Vyc2l2ZSA9IFRSVUUpCmBgYAoKKioqCgojIyBOb3RlCgo8c3R5bGU+CmRpdi5ibHVlIHsgYmFja2dyb3VuZC1jb2xvcjojNWI4ZThhOyBib3JkZXItcmFkaXVzOiA1cHg7IHBhZGRpbmc6IDIwcHg7fQo8L3N0eWxlPgo8ZGl2IGNsYXNzID0gImJsdWUiPgo8Zm9udCBzaXplPSI0Ij5UaGlzIGRvY3VtZW50IGlzICAqKmludGVyYWN0aXZlKiouIFlvdSBjYW4gKipzb3J0IGFuZCBzY3JvbGwqKiB0aHJvdWdoIG1vc3Qgb2YgdGhlIHRhYmxlcyBhbmQgZmlndXJlcy4gSW4gdGhlIHVwcGVyIHJpZ2h0IGhhbmQgY29ybmVyIG9mIHRoZSBmcm9udCBwYWdlIGlzIGEgYENvZGVgIGJ1dHRvbi4gVXNlIHRoaXMgdG8gc2hvdyBvciBoaWRlIGFsbCB0aGUgY29kZSBpbiB0aGUgZG9jdW1lbnQgKGRlZmF1bHQgaXMgaGlkZSkgYXMgd2VsbCBhcyBkb3dubG9hZCB0aGUgYC5SbWRgIGZpbGUgd2hpY2ggeW91IGNhbiB1c2UgdG8gZXh0cmFjdCB0aGUgY29kZS4KPC9kaXY+CgojIyMgQWJicmV2aWF0aW9ucyAgCgoqICoqQWdncmVnYXRpb24gU2NvcmUgKEFTKSoqOiBPY2N1cnJlbmNlICh0aGUgbnVtYmVyIG9mIGNvdW50cyBvYnNlcnZlZCBpbiAKdGhlIGFnZ3JlZ2F0aW9uIGNoYW1iZXIpIGZvciAyNGggb3IgNzJoIG9ic2VydmF0aW9uLgoKKiBbQW1wbGljb24gU2VxdWVuY2UgVmFyaWFudF0oaHR0cHM6Ly93d3cubmF0dXJlLmNvbS9hcnRpY2xlcy9pc21lajIwMTcxMTkpe3RhcmdldD0iX2JsYW5rIn0gKCoqQVNWKiopOiBFeGFjdCBzZXF1ZW5jZSB2YXJpYW50LS0tYW5hbG9nb3VzIHRvIGFuIE9UVS0tLWJ1dCB3aXRoIHNpbmdsZSBudWNsZW90aWRlIHJlc29sdXRpb24uIAoKIyMjIFN0dWR5IGdvYWxzCjEuIFRlc3RpbmcgYW4gb3BlcmF0aW5nIHN5c3RlbSBvZiBhIG11bHRpcGxlIGhpZ2gtc2Vuc2l0aXZlIGNhbWVyYXMgZm9yIDcyaApjYXB0dXJpbmcgdGhlIHNpbXVsdGFuZW91cyBwb3NpdGlvbnMgb2YgNjYgZmVtYWxlcyAqRi4gYXVyaWN1bGFyaWEqIChbVmFuIE1leWVsIGV0IGFsLiwgKDIwMjIpXShodHRwczovL2RvaS5vcmcvMTAuMTAxNi9qLmFuYmVoYXYuMjAyMi4wOS4wMDMpKS4KCjIuIENvbmZpcm0gdGhlIHJlcGVhdGFiaWxpdHkgb2YgdGhlIGFnZ3JlZ2F0aW9uIGxldmVsIGF0IGluZGl2aWR1YWwgc2NhbGUgKCppLmUuKiwgb3ZlciA3MmgpIGFuZCBiZXR3ZWVuIGluZGl2aWR1YWwgKCppLmUuLCogYmV0d2VlbiBpbmRlcGVuZGVudCB3ZWVrcyBvZiBleHBlcmltZW50cykuCgozLiBBbmFseXNlIHRoZSBwb3RlbnRpYWwgaW5mbHVlbmNlIG9mIGd1dCBtaWNyb2Jpb3RhIG9uIHRoZSBsZXZlbCBvZiBhZ2dyZWdhdGlvbiB0aHJvdWdoCm1pY3JvYmlhbCBkaXZlcnNpdHkgY29tcGFyaXNvbnMuCgo0LiBQZXJmb3JtIGEgZ3V0IHRyYW5zcGxhbnQgZXhwZXJpbWVudCBpbiBhIDJ4MiBmdWxsLWZhY3RvcmlhbCBkZXNpZ24sIGluIHdoaWNoIHdlIGZlZCBoaWdoLSBvciBsb3ctYWdncmVnYXRpb24gZmVtYWxlcyB3aXRoIHRoZSBndXQgb2YgaGlnaC0gb3IgbG93LSBhZ2dyZWdhdGlvbiBmZW1hbGVzIGFuZCB0aGVuIG1lYXN1cmVkIHRoZSBhZ2dyZWdhdGlvbiBzY29yZSBvZiB0aGUgcmVjaXBpZW50IGZlbWFsZXMuCgoqKioKCjxhIGlkPSJiYWNrIHRvIHRvcCI+PC9hPiAKCiMjIFdvcmtmbG93IG92ZXJ2aWV3CgojIyMjIFtQYXJ0IEk6IERhdGEgcHJlcGFyYXRpb25dKCNQYXJ0IEk6IERhdGEgcHJlcGFyYXRpb24pCgpUaGUgZGF0YSBhcmUgbG9jYXRlZCBpbiB0aHJlZSBmaWxlcyBhdmFpbGFibGUgaW4gU3VwcGxlbWVudGFyeSBNYXRlcmlhbCBvZiB0aGUgYXJ0aWNsZS4KCiogKiphZ2dyZWdhdGlvbl9kYXRhKiogOiBUaGUgYWdncmVnYXRpb25fZGF0YSBjb250YWlucyBpbmZvcm1hdGlvbiBvZiBhbGwgaW5kaXZpZHVhbHMgZm9yIGVhY2ggaG91ciB3aXRoIGZ1cnRoZXIgYmlvbG9naWNhbCBtZWFzdXJlcy4KCiogKipwc19yZmYqKiA6IFRoZSBwaHlsb3NlcSBvYmplY3QgYWxyZWFkeSBub3JtYWxpemVkIGF0ICoqMTAgMTgwIHNlcXVlbmNlcyoqICgqaS5lLiwqIHRoZSBtaW5pbXVtIHNhbXBsZSBzdW0gb2YgdGhlIGRhdGFzZXQpIGZvciBhIGZpbmFsIGRhdGFzZXQgY29uc3RpdHV0ZWQgYnkgKiozOTcgMDIwIHNlcXVlbmNlcyAoMSA1OTMgQVNWcykqKiBmb3IgMTkgbG93LSBhbmQgMjAgaGlnaC1hZ2dyZWdhdGlvbiBmZW1hbGVzIChvbmUgc2FtcGxlIGRpZCBub3QgYW1wbGlmeSkuCgoqICoqdHJhbnNwbGFudF9kYXRhKiogOiBUaGUgZGF0YSBjb25jZXJuaW5nIHRoZSBpbmRpdmlkdWFsIGludm9sdmVkIGluIHRoZSBndXQgdHJhbnNwbGFudCB3aXRoIHRoZWlyIEFTIGJlZm9yZSB0aGUgZ3V0IHRyYW5zcGxhbnQgKGZvciBkb25vcnMgYW5kIHJlY2lwaWVudHMpLgoKKiAqKmFnZ3JlZ2F0aW9uX2FmdGVyX3RyYW5zcGxhbnQqKiA6IFRoZSBkYXRhIGNvbmNlcm5pbmcgdGhlIHJlY2lwaWVudHMgaW5kaXZpZHVhbCBhZ2dyZWdhdGlvbiBkYXRhIGZvciBlYWNoIGhvdXIuIAoKPiBJbiBjYXNlIHlvdSB3YW50IHRvIHByb2Nlc3MgdGhlIHJlYWRzIGZyb20geW91ciBvd24sIHNlZSAqKltQYXJ0IElJSTogTWljcm9iaW90YSBkaXZlcnNpdHkgaW5mbHVlbmNlIG9uIEFnZ3JlZ2F0aW9uIGxldmVsXSgjUGFydCBJSUk6IE1pY3JvYmlvdGEgZGl2ZXJzaXR5IGluZmx1ZW5jZSBvbiBBZ2dyZWdhdGlvbiBsZXZlbCkqKi4KCiMjIyMgW1BhcnQgSUk6IFZhcmlhYmlsaXR5IGludHJhL2ludGVyIGluZGl2aWR1YWxzXSgjUGFydCBJSTogVmFyaWFiaWxpdHkgaW50cmEvaW50ZXIgaW5kaXZpZHVhbHMpCgpXZSBmaXJzdCB0ZXN0ZWQgd2hldGhlciB0aGUgbnVtYmVyIG9mIGZlbWFsZXPigJkgb2NjdXJyZW5jZXMgaW4gdGhlIGFnZ3JlZ2F0aW9uIGNoYW1iZXIgKCppLmUuKiwgQWdncmVnYXRpb24gU2NvcmUpIHdhcyAqKnJlcGVhdGFibGUgb3ZlciB0aGUgdGhyZWUgZGF5cyoqIG9mIG1lYXN1cmVtZW50cyBhbmQvb3Igb3ZlcmFsbCBjaGFuZ2VkICoqdGhyb3VnaG91dCB0aGUgc2l4IHJlY29yZGluZyBzZXNzaW9ucyoqIChmcm9tIHRoZSAxMCBKdW5lIDIwMjIgdG8gdGhlIDI1IEp1bHkgMjAyMikuIFRvIHRoaXMgZW5kLCB3ZSBjb25kdWN0ZWQgYSBzZXJpZXMgb2YgdGhyZWUgTGluZWFyIE1vZGVscyAoTE1zKSwgaW4gd2hpY2ggd2UgZW50ZXJlZCB0aGUgQWdncmVnYXRpb24gU2NvcmUgb2YgZWFjaCBkYXkgYXMgdGhlIHJlc3BvbnNlIHZhcmlhYmxlLCBhbmQgdGhlIEFnZ3JlZ2F0aW9uIFNjb3JlIG9mIG9uZSBvZiB0aGUgdHdvIG90aGVyIGRheSAoY29udGludW91cyksIHRoZSB3ZWVrIG9mIHRoZSByZWNvcmRpbmcgc2Vzc2lvbiAoY2F0ZWdvcnkpIGFuZCB0aGUgaW50ZXJhY3Rpb24gYXMgZXhwbGFuYXRvcnkgdmFyaWFibGVzLgoKCj4gUHM6IEZvciBlYWNoIHN0YXRpc3RpY2FsIG1vZGVsLCB3ZSBjaGVja2VkIHRoYXQgYWxsIGFzc3VtcHRpb25zIHdlcmUgZnVsZmlsbGVkIHVzaW5nIHRoZSBESEFSTWEgUiBwYWNrYWdlIHYwLjQuNiBbKEhhcnRpZywgMjAyMildKGh0dHA6Ly9mbG9yaWFuaGFydGlnLmdpdGh1Yi5pby9ESEFSTWEvKS4KCiMjIyMgW1BhcnQgSUlJOiBNaWNyb2Jpb3RhIGRpdmVyc2l0eSBhbmQgQWdncmVnYXRpb24gbGV2ZWxdKCNQYXJ0IElJSTogTWljcm9iaW90YSBkaXZlcnNpdHkgYW5kIEFnZ3JlZ2F0aW9uIGxldmVsKQoKVGhpcyBwYXJ0IGFsbG93cyB0byBwZXJmb3JtIGEgZGVzY3JpcHRpdmUgYW5hbHlzaXMgb2YgdGhlIG1pY3JvYmlvdGEgZnJvbSB0aGUgKipwc19yZmYqKiBvYmplY3QgYW5kIHRvIGZ1cnRoZXIgY2FsY3VsYXRlIGJvdGggYWxwaGEgYW5kIGJldGEgZGl2ZXJzaXR5IHRha2luZyBpbnRvIGFjY291bnQgdGhlIHRheG9ub215IGFuZCB0aGUgcGh5bG9nZW55LCBmb3IgcXVhbnRpdGF0aXZlIGFuZCBxdWFsaXRhdGl2ZSBpbmZvcm1hdGlvbnMgZnJvbSBtaWNyb2JpYWwgY29tbXVuaXRpZXMuCgojIyMjIFtQYXJ0IElWOiBHdXQgdHJhbnNwbGFudGF0aW9uXSgjUGFydCBJVjogR3V0IHRyYW5zcGxhbnRhdGlvbikKCkxhc3QsIHRoaXMgIHBhcnQgY29uY2VybnMgdGhlIGd1dCB0cmFuc3BsYW50IGV4cGVyaW1lbnQgaW4gd2hpY2ggd2UgZmVkIGhpZ2gtIG9yIGxvdy1hZ2dyZWdhdGlvbiBmZW1hbGVzIHdpdGggdGhlIGd1dCBvZiBoaWdoLSBvciBsb3ctIGFnZ3JlZ2F0aW9uIGZlbWFsZXMgYW5kIHRoZW4gbWVhc3VyZWQgdGhlIGFnZ3JlZ2F0aW9uIHNjb3JlIG9mIHRoZSByZWNpcGllbnQgZmVtYWxlcy4KCioqKgoKPGEgaWQ9IlBhcnQgSTogRGF0YSBwcmVwYXJhdGlvbiI+PC9hPgoKIyMgUGFydCBJOiBEYXRhIHByZXBhcmF0aW9uCltiYWNrIHRvIHRvcF0oI2JhY2sgdG8gdG9wKQoKIyMjIExvYWQgdGhlIGZpbGVzCgpGaXJzdCwgZG93bmxvYWQgdGhlIG1ldGFkYXRhIGZpbGUgaW4gdGhlICoqL2Rpcl9kYXRhc2V0KiogZm9sZGVyIGFuZCBsb2FkIGl0IHRvIGNyZWF0ZSBhIApuZXcgdGFibGUgd2l0aCAqKkFnZ3JlZ2F0aW9uIFNjb3JlKiogZm9yIGVhY2ggc2FtcGxlIGF0IGVhY2ggZXhwZXJpbWVudGFsIGRheSAoRDEgPSBkYXkxLApEMiA9IGRheTIgYW5kIEQzID0gZGF5MykuCgpgYGB7ciBmaWxlIGxvYWQsIGV2YWwgPSBUfQphZ2dyZWdhdGlvbl9kYXRhIDwtIHJlYWQudGFibGUoZmlsZSA9IHBhc3RlMChkaXJfZGF0YXNldCwgImFnZ3JlZ2F0aW9uX2RhdGEudHh0IiksIGhlYWRlciA9IFQsIHNlcCA9ICJcdCIpCmBgYAoKYGBge3IgQVMgZGF0YSBmaWxlLCAgZXZhbCA9IFQsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CkFTX2RhdGEgPC0gbGVmdF9qb2luKAogIGFnZ3JlZ2F0aW9uX2RhdGEgJT4lIAogICAgZ3JvdXBfYnkoSUQsZGF5LHNhbXBsaW5nX2RhdGUpICU+JSAKICAgIHN1bW1hcmlzZShBZ2dyZWdhdGlvbiA9IHN1bShhdHRyYWN0aW9uKSkgJT4lCiAgICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gZGF5LCB2YWx1ZXNfZnJvbSA9IEFnZ3JlZ2F0aW9uKSAlPiUKICAgIG11dGF0ZShBUzcyID0gc3VtKGNfYWNyb3NzKEQxOkQzKSkpICU+JQogICAgYXMuZGF0YS5mcmFtZSgpLAogIGFnZ3JlZ2F0aW9uX2RhdGEgJT4lIAogICAgc2VsZWN0KCFjKHNhbXBsaW5nX2RhdGUsIGF0dHJhY3Rpb24sIGRheSwgdGltZV9leHApKSAlPiUgCiAgICB1bmlxdWUoKSwKICBieSA9ICJJRCIpCgp3cml0ZS50YWJsZShBU19kYXRhLGZpbGUgPSBwYXN0ZTAoZGlyX2RhdGFzZXQsICJBU19kYXRhLnR4dCIpLCBzZXAgPSJcdCIsIHJvdy5uYW1lcyA9IEYpCmBgYAoKIyMjIFByZXNlbnRhdGlvbiBvZiB0aGUgZGF0YQoKVGhlIGFnZ3JlZ2F0aW9uX2RhdGEgaXMgY29uc3RpdHV0ZWQKKiBbKipJRCoqXXt0YXJnZXQ9Il9ibGFuayJ9IDogVGhlIElEIG9mIHRoZSBmZW1hbGUgc2FtcGxlLgoKKiBbKipzYW1wbGluZ19kYXRlKipde3RhcmdldD0iX2JsYW5rIn06IFNhbXBsaW5nIGRhdGUgaXMgYXNzb2NpYXRlZCB0byBhIHJ1biBudW1iZXIuCgoqIFsqKmF0dHJhY3Rpb24qKl17dGFyZ2V0PSJfYmxhbmsifTogUHJlc2VuY2UgKDEpIG9yIGFic2VuY2UgKDApIGluIHRoZSBhZ2dyZWdhdGlvbiBjaGFtYmVyIGF0IHRoZSBvYnNlcnZhdGlvbiB0LgoKKiBbKip0aW1lX2V4cCoqXXt0YXJnZXQ9Il9ibGFuayJ9OiBPYnNlcnZhdGlvbiBudW1iZXIgKGJldHdlZW4gMSBhbmQgNzIpCgoqIFsqKmRheSoqXXt0YXJnZXQ9Il9ibGFuayJ9OiBUaGUgZGF5IG51bWJlciBvciB0aGUgZXhwZXJpbWVudCAoRDEsRDIgb3IgRDMpIGFsbG93aW5nIHRvIGNhbGN1bCB0aGUgQVMgYXQgKipEMSwgRDIgYW5kIEQzIGluIHRoZSBBU19kYXRhKiouCgoqIFsqKmFnZ3JlZ2F0aW9uKipde3RhcmdldD0iX2JsYW5rIn06IHRoZSBmZW1hbGUgaXMgdmVyeSBzdHVjayB0byB0aGUgYXR0cmFjdGl2ZSBjaGFtYmVyIChZID0geWVzKSBvciBub3QgKE4gPSBubykgd2hlbiBzaGUgaXMgaW4gdGhlIGFnZ3JlZ2F0aW9uIGNoYW1iZXIuIFRoZSB0b3RhbCBvdmVyIHRoZSA3MiBvYnNlcnZhdGlvbnMgYWxsb3dzIHRvIG9idGFpbiB0aGUgKipBUzcyIGluIHRoZSBBU19kYXRhKiouCgoKCmBgYHtyIEFTIGRhdGEgLCBldmFsID0gVCwgd2FybmluZyA9IEZBTFNFLCBmaWcuYWxpZ24gPSAiY2VudGVyIiwgbWVzc2FnZT1GQUxTRX0KQVNfZGF0YSA8LSByZWFkLnRhYmxlKGZpbGUgPSBwYXN0ZTAoZGlyX2RhdGFzZXQsICJBU19kYXRhLnR4dCIpLCBoZWFkZXIgPSBULCBzZXAgPSAiXHQiKQpkYXRhdGFibGUoQVNfZGF0YSAlPiUKICAgICAgICAgICAgZmlsdGVyKHN0YXR1cyAlaW4lIGMoIkhpZ2giLCJMb3ciKSksCiAgICAgICAgICByb3duYW1lcyA9IEZBTFNFLCAKICAgICAgICAgIHdpZHRoID0gIjEwMCUiLAogICAgICAgICAgY29sbmFtZXMgPSBjKCJJRCIsICJTYW1wbGluZyBkYXRlIiwiQVMgYXQgRDEiLCAiQVMgYXQgRDIiLCAiQVMgYXQgRDMiLCAiQVMgb3ZlciA3MmgiLCAiQWdncmVnYXRpb24gbGV2ZWwiKSwgCiAgICAgICAgICBjYXB0aW9uID0gaHRtbHRvb2xzOjp0YWdzJGNhcHRpb24oc3R5bGUgPSAiY2FwdGlvbi1zaWRlOiAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBib3R0b207IHRleHQtYWxpZ246IGxlZnQ7IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlRhYmxlOiAiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBodG1sdG9vbHM6OmVtKCJzaG9ydCB0YWJsZSBwcmVzZW50YXRpb24gZm9yIHRoZSBmZW1hbGVzIHByZXNlbnRpbmcgbG93LSBhbmQgaGlnaC1hZ2dyZWdhdGlvbiBsZXZlbHMuIikpLCAKICAgICAgICAgIGV4dGVuc2lvbnMgPSAiQnV0dG9ucyIsIAogICAgICAgICAgb3B0aW9ucyA9IGxpc3QoY29sdW1uRGVmcyA9IAogICAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0KGxpc3QoY2xhc3NOYW1lID0gImR0LWxlZnQiLCB0YXJnZXRzID0gMCkpLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGluaXRDb21wbGV0ZSA9IEpTKCJmdW5jdGlvbihzZXR0aW5ncywganNvbikgeyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiJCh0aGlzLmFwaSgpLnRhYmxlKCkuaGVhZGVyKCkpLmNzcyh7J2JhY2tncm91bmQtY29sb3InOiAnIzAwMCcsICdjb2xvcic6ICcjZmZmJ30pOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJCgnYm9keScpLmNzcyh7J2ZvbnQtZmFtaWx5JzogJ0NhbGlicmknfSk7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAkKCdib2R5JykuY3NzKHsnZm9udC1jb2xvcic6ICcjZmZmJ30pOyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAifSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgZG9tID0gIkJsZnJ0aXAiLCBwYWdlTGVuZ3RoID0gNSwgCiAgICAgICAgICAgICAgICAgICAgICAgICBsZW5ndGhNZW51ID0gYyg1LCAxMCwgMjUsIDUwKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICBidXR0b25zID0gYygiY3N2IiwgImNvcHkiKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICBzY3JvbGxYID0gVFJVRSwgc2Nyb2xsQ29sbGFwc2UgPSBUUlVFKSkgJT4lCiAgZm9ybWF0U3R5bGUoIklEIiwgYmFja2dyb3VuZENvbG9yID0gJ3doaXRlJyklPiUKICBmb3JtYXRTdHlsZSgic2FtcGxpbmdfZGF0ZSIsIGJhY2tncm91bmRDb2xvciA9ICd3aGl0ZScpJT4lCiAgZm9ybWF0U3R5bGUoIkQxIiwgYmFja2dyb3VuZENvbG9yID0gJ3doaXRlJyklPiUKICBmb3JtYXRTdHlsZSgiRDIiLCBiYWNrZ3JvdW5kQ29sb3IgPSAnd2hpdGUnKSU+JQogIGZvcm1hdFN0eWxlKCJEMyIsIGJhY2tncm91bmRDb2xvciA9ICd3aGl0ZScpJT4lCiAgZm9ybWF0U3R5bGUoIkFTNzIiLCBiYWNrZ3JvdW5kQ29sb3IgPSAnd2hpdGUnKSU+JQogIGZvcm1hdFN0eWxlKCJzdGF0dXMiLCBiYWNrZ3JvdW5kQ29sb3IgPSAnd2hpdGUnKQoKYGBgCgojIyBQYXJ0IElJOiBWYXJpYWJpbGl0eSBpbnRyYS9pbnRlciBpbmRpdmlkdWFscwpbYmFjayB0byB0b3BdKCNiYWNrIHRvIHRvcCkKCiMjIyBBZ2dyZWdhdGlvbiBkaXN0cmlidXRpb24KCldlIGlsbHVzdHJhdGVkIHRoZSBhZ2dyZWdhdGlvbiBiZXR3ZWVuIGluZGl2aWR1YWxzIGluIDM4NSBmZW1hbGUgZWFyd2lncyB0byBkZXRlcm1pbmUgZW1waXJpY2FsbHkgdGhlIHZhcmlhYmlsaXR5IG9mIHRoaXMgYmVoYXZpb3VyIGFuZCBpdHMgY29uc3RhbnQgaW4gdGltZSB1bmRlciBzYW1lIGVudmlyb25tZW50YWwgY29uZGl0aW9ucyAoaGVyZSwgdGhlIHNhbWUgMyBhdHRyYWN0YW50cyBmZW1hbGVzKS4KCmBgYHtyIGZyZXF1ZW5jeSBkaXN0cmlidXRpb24gLCBldmFsID0gVCwgcmVzdWx0cz0naGlkZSd9CkFTX2RhdGEgPC0gcmVhZC50YWJsZShmaWxlID0gcGFzdGUwKGRpcl9kYXRhc2V0LCAiQVNfZGF0YS50eHQiKSwgaGVhZGVyID0gVCwgc2VwID0gIlx0IikKCkZpZ19BUzcyIDwtIEFTX2RhdGEgJT4lCiAgZ2dwbG90KGFlcyh4PUFTNzIpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHkgPSBhZnRlcl9zdGF0KGRlbnNpdHkpKSxhbHBoYSA9IDAuNSwgYmlucyA9IDcyICxjb2xvciA9ICJibGFjayIsbGluZXdpZHRoID0gMC41KSArCiAgdGhlbWVfYncoKSArCiAgdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJzZXJpZiIsc2l6ZSA9IDEyKSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiLHNpemUgPSAxMiksCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplPTE0LCBmYW1pbHkgPSAic2VyaWYiKSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZT0xNCwgZmFtaWx5ID0gInNlcmlmIiksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgeGxhYigiQWdncmVnYXRpb24gU2NvcmUiKSArCiAgeWxhYigiRnJlcXVlbmN5IikKRmlnX0FTNzIKCnBkZihmaWxlID0gcGFzdGUwKGRpcl9ncmFwaCwgIkZpZ19BUzcyLnBkZiIpLCBoZSA9IDcsIHdpID0gNykKRmlnX0FTNzIKZGV2Lm9mZigpCmBgYAoKV2UgdGVzdCBpZiB0aGUgYWdncmVnYXRpb24gaXMgYSBkZXRlcm1pbmlzdGljIG9yIHJhbmRvbSB0cmFpdCBieSBjb21wYXJpbmcgdGhlIGRpc3RyaWJ1dGlvbiB3aXRoIGEgcmFuZG9tIGRpc3RyaWJ1dGlvbiBvZiBBUyBhdCA3Mmggd2UgY29tcGFyZWQgdG8gb3VyIG9ic2VydmVkIEFTNzIgd2l0aCBhIEtoaTIgdGVzdCAoKippLmUuLCoqIGFrYSAxLzMgY2hhbmNlIG9mIDcyIGNoYW5jZSB0byBiZSBpbiB0aGUgYWdncmVnYXRpb24gY2hhbWJlcikuIFRoaXMgY29uZmlybSB0aGUgZmFjdCB0aGF0IHRoZSBiZWhhdmlvdXIgb2YgdGhlIGZlbWFsZSBpcyBub3QgcmFuZG9tLgoKYGBge3Igbm9ybWFsIGRpc3RyaWJ1dGlvbiAsIGV2YWwgPSBUfQpBU19kYXRhIDwtIHJlYWQudGFibGUoZmlsZSA9IHBhc3RlMChkaXJfZGF0YXNldCwgIkFTX2RhdGEudHh0IiksIGhlYWRlciA9IFQsIHNlcCA9ICJcdCIpCmFnZ3JlZ2F0aW9uX2RhdGEgPC0gcmVhZC50YWJsZShmaWxlID0gcGFzdGUwKGRpcl9kYXRhc2V0LCJhZ2dyZWdhdGlvbl9kYXRhLnR4dCIpLCBoZWFkZXIgPSBULCBzZXAgPSAiXHQiKQpBUyA8LSBjKDAsMSkKQVNfdGggPC0gc2FtcGxlKEFTLCBzaXplID0gNzIqMzg1LCByZXBsYWNlID0gVCwgcD0gYygyLzMsMS8zKSkgIyBpZiByYW5kb20sIDIvMyBjaGFuY2UgdG8gYmUgaW4gdGhlIG5vbi1hZ2dyZWdhdGl2ZSBjaGFtYmVyIGFuZCAxLzMgdG8gYmUgaW4gdGhlIGFnZ3JlZ2F0aXZlIGNoYW1iZXIKYWdncmVnYXRpb25fdGggPC0gYWdncmVnYXRpb25fZGF0YQphZ2dyZWdhdGlvbl90aCRhdHRyYWN0aW9uID0gQVNfdGgKCkFTX2RhdGFfdGggPC0gYWdncmVnYXRpb25fdGggJT4lIAogIGdyb3VwX2J5KElELGRheSxzYW1wbGluZ19kYXRlKSAlPiUgCiAgc3VtbWFyaXNlKEFnZ3JlZ2F0aW9uID0gc3VtKGF0dHJhY3Rpb24pKSAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gZGF5LCB2YWx1ZXNfZnJvbSA9IEFnZ3JlZ2F0aW9uKSAlPiUKICBtdXRhdGUoQVM3MiA9IHN1bShjX2Fjcm9zcyhEMTpEMykpKSAlPiUKICBhcy5kYXRhLmZyYW1lKCkKCmNvbXAgPC0gY2JpbmQoIkFTX29icyIgPSBBU19kYXRhJEFTNzIsIAogICAgICAgICAgICAgICJBU190aCIgPSBBU19kYXRhX3RoJEFTNzIpICU+JQogIHQoKSAlPiUKICBkYXRhLmZyYW1lKCkKCmNvbG5hbWVzKGNvbXApIDwtIEFTX2RhdGEkSUQKY2hpc3EudGVzdChjb21wKQpgYGAKCiMjIyBBZ2dyZWdhdGlvbiByZXBlYXRhYmlsaXR5CgpUbyBjb25maXJtIHRoZSByb2J1c3RuZXNzIG9mIG91ciBhZ2dyZWdhdGlvbiBzY29yZSAoQVMpLCB3ZSBmaXJzdCBhbmFseXNlZCB0aGUgcmVwZWF0YWJpbGl0eSBvZiB0aGlzIHZhbHVlIG92ZXIgdGhlIHRocmVlIGRheXMgb24gd2hpY2ggbWVhc3VyZW1lbnRzIHdlcmUgdGFrZW4gdXNpbmcgdGhyZWUgTGluZWFyIG1vZGVscyAoTE0pLiBJbiB0aGVzZSBtb2RlbHMsIHRoZSByZXNwb25zZSB2YXJpYWJsZSB3YXMgdGhlIEFTIG1lYXN1cmVkIGVpdGhlciBvbiBkYXkgMiwgMyBvciAzLCB3aGVyZWFzIHRoZSBleHBsYW5hdG9yeSB2YXJpYWJsZXMgd2VyZSB0aGUgQVMgbWVhc3VyZWQgb24gZGF5IDEsIDIgb3IgMSwgcmVzcGVjdGl2ZWx5LCB0aGUgd2VlayBvZiB0aGUgcmVjb3JkaW5nIHNlc3Npb24gKHNpeCBzZXNzaW9uczogY2F0ZWdvcmljYWwgZmFjdG9yKSBhbmQgdGhlIGludGVyYWN0aW9uIGJldHdlZW4gdGhlc2UgdHdvIHZhcmlhYmxlcy4KCj4gUHM6IEZvciBlYWNoIHN0YXRpc3RpY2FsIG1vZGVsLCB3ZSBjaGVja2VkIHRoYXQgYWxsIGFzc3VtcHRpb25zIHdlcmUgZnVsZmlsbGVkIHVzaW5nIHRoZSBESEFSTWEgUiBwYWNrYWdlIHYwLjQuNiBbKEhhcnRpZywgMjAyMildKGh0dHA6Ly9mbG9yaWFuaGFydGlnLmdpdGh1Yi5pby9ESEFSTWEvKS4gSGVyZSBpcyBhbiBleGFtcGxlIGZvciB0aGUgTE0gcnVuIG9uIHRoZSBEMiBhbmQgRDEuCgpgYGB7ciBESEFSTUEgLCBldmFsID0gVCwgcmVzdWx0cz0gJ2hpZGUnfQpBU19kYXRhIDwtIHJlYWQudGFibGUoZmlsZSA9IHBhc3RlMChkaXJfZGF0YXNldCwgIkFTX2RhdGEudHh0IiksIHNlcCA9Ilx0IiwgaGVhZGVyID0gVCkKCmxtX0QyRDEgPC0gbG0oRDIgfiBEMSAqIHNhbXBsaW5nX2RhdGUgLCBkYXRhID0gQVNfZGF0YSkKc2ltdWxhdGlvbk91dHB1dF9EMkQxIDwtIHNpbXVsYXRlUmVzaWR1YWxzKGZpdHRlZE1vZGVsID0gbG1fRDJEMSwgcGxvdCA9IFQpCnRlc3RSZXNpZHVhbHMobG1fRDJEMSkKYGBgCgpBcyBzdWNoLCB3ZSB0ZXN0ZWQgZWl0aGVyIHRoZSBBUyBhdCBEMiB3YXMgZXhwbGFpbmVkIGJ5IHRoZSBBUyBhdCBEMSB3aGF0ZXZlciB0aGUgc2FtcGxpbmcgcnVuLi4uCgpgYGB7ciBEMiB+IEQxLCBldmFsID0gVH0KQVNfZGF0YSA8LSByZWFkLnRhYmxlKGZpbGUgPSBwYXN0ZTAoZGlyX2RhdGFzZXQsICJBU19kYXRhLnR4dCIpLCBzZXAgPSJcdCIsIGhlYWRlciA9IFQpCgpsbV9EMkQxIDwtIGxtKEQyIH4gRDEgKiBzYW1wbGluZ19kYXRlICwgZGF0YSA9IEFTX2RhdGEpCmNhcjo6QW5vdmEobG1fRDJEMSkKYGBgCgpFaXRoZXIgdGhlIEFTIGF0IEQzIHdhcyBleHBsYWluZWQgYnkgdGhlIEFTIGF0IEQyIHdoYXRldmVyIHRoZSBzYW1wbGluZyBydW4uLi4KCmBgYHtyIEQzfiBEMiwgZXZhbCA9IFR9CkFTX2RhdGEgPC0gcmVhZC50YWJsZShmaWxlID0gcGFzdGUwKGRpcl9kYXRhc2V0LCAiQVNfZGF0YS50eHQiKSwgc2VwID0iXHQiLCBoZWFkZXIgPSBUKQoKbG1fRDNEMiA8LSBsbShEMyB+IEQyICogc2FtcGxpbmdfZGF0ZSAsIGRhdGEgPSBBU19kYXRhKQpjYXI6OkFub3ZhKGxtX0QzRDIpICAKYGBgCgpBbmQgZmluYWxseSBlaXRoZXIgdGhlIEFTIGF0IEQzIHdhcyBleHBsYWluZWQgYnkgdGhlIEFTIGF0IEQxIHdoYXRldmVyIHRoZSBzYW1wbGluZyBydW4uCmBgYHtyIEQzIH4gRDEsIGV2YWwgPSBUfQpBU19kYXRhIDwtIHJlYWQudGFibGUoZmlsZSA9IHBhc3RlMChkaXJfZGF0YXNldCwgIkFTX2RhdGEudHh0IiksIHNlcCA9Ilx0IiwgaGVhZGVyID0gVCkKCmxtX0QzRDEgPC0gbG0oRDMgfiBEMSAqIHNhbXBsaW5nX2RhdGUgLCBkYXRhID0gQVNfZGF0YSkKY2FyOjpBbm92YShsbV9EM0QxKQpgYGAKCgpgYGB7ciBkYXlzIGdyYXBoLCBldmFsID0gVCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLndpZHRoPTE2ICwgZmlnLmhlaWdodD01LCByZXN1bHRzPSdoaWRlJ30KQVNfZGF0YSA8LSByZWFkLnRhYmxlKGZpbGUgPSBwYXN0ZTAoZGlyX2RhdGFzZXQsICJBU19kYXRhLnR4dCIpLCBzZXAgPSJcdCIsIGhlYWRlciA9IFQpCiMjIyBfIEJldHdlZW4gRDEgYW5kIEQyIC0tLS0KRDFfRDJfcnVucyA8LSBBU19kYXRhICU+JQogIGdncGxvdChhZXMoeCA9IEQyLCB5ID0gRDEsIGNvbG9yID0gc2FtcGxpbmdfZGF0ZSkpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IHNhbXBsaW5nX2RhdGUpLCBhbHBoYSA9IDAuNSkgKwogIGdlb21fc21vb3RoKG1ldGhvZD0gbG0sIHNlPVQsIGZ1bGxyYW5nZT1UKSArCiAgZ2dwdWJyOjpjb2xvcl9wYWxldHRlKCJ1Y3NjZ2IiKSsKICB5bGFiKCJBZ2dyZWdhdGlvbiBTY29yZSAoRGF5IDIpIikgKwogIHhsYWIoIkFnZ3JlZ2F0aW9uIFNjb3JlIChEYXkgMSkiKSArCiAgdGhlbWVfYncoKSArCiAgdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJzZXJpZiIsc2l6ZSA9IDE2KSwgCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gInNlcmlmIixzaXplID0gMTYpLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJzZXJpZiIsc2l6ZSA9IDE2KSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gInNlcmlmIixzaXplID0gMTgpLAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiLHNpemUgPSAxOCksCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gInNlcmlmIiwgc2l6ZSA9IDE2KSwKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gInNlcmlmIiwgc2l6ZSA9IDE4KSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKIyMjIF8gQmV0d2VlbiBEMSBhbmQgRDMgLS0tLQpEMV9EM19ydW5zIDwtIEFTX2RhdGEgJT4lCiAgZ2dwbG90KGFlcyh4ID0gRDEsIHkgPSBEMywgY29sb3IgPSBzYW1wbGluZ19kYXRlKSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gc2FtcGxpbmdfZGF0ZSksIGFscGhhID0gMC41KSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSBsbSwgc2U9VCwgZnVsbHJhbmdlPVQpICsKICBnZ3B1YnI6OmNvbG9yX3BhbGV0dGUoInVjc2NnYiIpKwogIHlsYWIoIkFnZ3JlZ2F0aW9uIFNjb3JlIChEYXkgMykiKSArCiAgeGxhYigiQWdncmVnYXRpb24gU2NvcmUgKERheSAxKSIpICsKICB0aGVtZV9idygpICsKICB0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gInNlcmlmIixzaXplID0gMTYpLCAKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiLHNpemUgPSAxNiksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gInNlcmlmIixzaXplID0gMTYpLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiLHNpemUgPSAxOCksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJzZXJpZiIsc2l6ZSA9IDE4KSwKICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiLCBzaXplID0gMTYpLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiLCBzaXplID0gMTgpLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgojIyMgXyBCZXR3ZWVuIEQyIGFuZCBEMyAtLS0tCkQyX0QzX3J1bnMgPC0gQVNfZGF0YSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBEMiwgeSA9IEQzLCBjb2xvciA9IHNhbXBsaW5nX2RhdGUpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBzYW1wbGluZ19kYXRlKSwgYWxwaGEgPSAwLjUpICsKICBnZW9tX3Ntb290aChtZXRob2Q9IGdsbSwgc2U9VCwgZnVsbHJhbmdlPVQpICsKICBnZ3B1YnI6OmNvbG9yX3BhbGV0dGUoInVjc2NnYiIpKwogIHlsYWIoIkFnZ3JlZ2F0aW9uIFNjb3JlIChEYXkgMykiKSArCiAgeGxhYigiQWdncmVnYXRpb24gU2NvcmUgKERheSAyKSIpICsKICB0aGVtZV9idygpICsKICB0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gInNlcmlmIixzaXplID0gMTYpLCAKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiLHNpemUgPSAxNiksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gInNlcmlmIixzaXplID0gMTYpLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiLHNpemUgPSAxOCksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJzZXJpZiIsc2l6ZSA9IDE4KSwKICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiLCBzaXplID0gMTYpLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiLCBzaXplID0gMTgpLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgpydW5zX2xlZ2VuZCA8LSBBU19kYXRhICU+JQogIGdncGxvdChhZXMoeCA9IEQyLCB5ID0gRDMsIGNvbG9yID0gc2FtcGxpbmdfZGF0ZSkpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IHNhbXBsaW5nX2RhdGUpLCBhbHBoYSA9IDAuNSkgKwogIGdlb21fc21vb3RoKG1ldGhvZD0gZ2xtLCBzZT1ULCBmdWxscmFuZ2U9VCkgKwogIGdncHVicjo6Y29sb3JfcGFsZXR0ZSgidWNzY2diIikrCiAgeWxhYigiQWdncmVnYXRpb24gU2NvcmUgKERheSAzKSIpICsKICB4bGFiKCJBZ2dyZWdhdGlvbiBTY29yZSAoRGF5IDIpIikgKwogIHRoZW1lX2J3KCkgKwogIHRoZW1lKGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiLHNpemUgPSAxNiksIAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJzZXJpZiIsc2l6ZSA9IDE2KSwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiLHNpemUgPSAxNiksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJzZXJpZiIsc2l6ZSA9IDE4KSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gInNlcmlmIixzaXplID0gMTgpLAogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJzZXJpZiIsIHNpemUgPSAxNiksCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJzZXJpZiIsIHNpemUgPSAxOCksCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKSsKICBsYWJzKGNvbG9yID0gIlNhbXBsaW5nIHdlZWsiKQoKRmlnX3JlcGVhdCA8LSBnZ2FycmFuZ2UobGVnZW5kLmdyb2IgPSBnZXRfbGVnZW5kKHJ1bnNfbGVnZW5kKSwKICAgICAgICAgIGxlZ2VuZCA9ICJyaWdodCIsCiAgICAgICAgICBEMV9EMl9ydW5zLEQxX0QzX3J1bnMsRDJfRDNfcnVucywKICAgICAgICAgIG5yb3cgPSAxLCBuY29sID0gMykKRmlnX3JlcGVhdAoKcGRmKGZpbGUgPSBwYXN0ZTAoZGlyX2dyYXBoLCAiRmlnX3JlcGVhdC5wZGYiKSwgaGUgPSA1LCB3aSA9IDE2KQpGaWdfcmVwZWF0CmRldi5vZmYoKQpgYGAKCgoKIyMgUGFydCBJSUk6IE1pY3JvYmlvdGEgZGl2ZXJzaXR5IGFuZCBBZ2dyZWdhdGlvbiBsZXZlbApbYmFjayB0byB0b3BdKCNiYWNrIHRvIHRvcCkKClRoaXMgaXMgdGhlIGJpb2luZm9ybWF0aWNhbCBwaXBlbGluZSB0byBwcm9jZXNzIHRoZSBsaWJyYXJpZXMgb3JpZ2luYXRpbmcgZnJvbSB0aGUgZ3V0IG1pY3JvYmlvdGEgCgo+IFRoZSBsaWJyYXJpZXMgdXNlZCBpbiB0aGlzIHN0dWR5IChhbmQgYXZhaWxhYmxlIGluIFtOQ0JJXShodHRwczovL2h0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvKXt0YXJnZXQ9Il9ibGFuayJ9IHVuZGVyIHRoZSBiaW9wcm9qZWN0IGFjY2Vzc2lvbiBuby4gKipQUkpOQTkzNjEzNioqKS4KQmVmb3JlIHN0YXJ0aW5nLCBsaWJyYXJpZXMgaGFkIHRvIGJlIGRlbXVsdGlwbGV4ZWQgYW5kIGxpbmtlcnMgd2l0aCBwcmltZXJzIHNlcXVlbmNlcyByZW1vdmVkIGJlZm9yZSBiZWluZyBwcm9jZXNzZWQgdGhyb3VnaCBbREFEQTIgcGlwZWxpbmVdKGh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvcG1jL2FydGljbGVzL1BNQzQ5MjczNzcvKXt0YXJnZXQ9Il9ibGFuayJ9IChDYWxsYWhhbiBldCBhbC4sIDIwMTYpLiBCcmllZmx5LCBzZXF1ZW5jZXMgYXJlIHRyaW1tZWQgYW5kIGZpbHRlcmVkIGZvciBxdWFsaXR5LCBkZXJlcGxpY2F0ZWQgYW5kIGluZmVycmVkIGluIEFTVnMgKEdsYXNzbWFuICYgTWFydGlueSwgMjAxOCkuIEZvcndhcmQgYW5kIHJldmVyc2UgQVNWcyBhcmUgbWVyZ2VkIGFuZCBmaW5hbGx5IGdlbmVyYXRlZCBpbnRvIGEgY291bnQgdGFibGUgd2hlcmUgY2hpbWVyYSBhcmUgaWRlbnRpZmllZCBhbmQgcmVtb3ZlZC4gVGhlIHRheG9ub215IGFzc2lnbm1lbnQgaXMgcGVyZm9ybWVkIHVzaW5nIHRoZSBbU0lMVkEgcmVmZXJlbmNlIGRhdGFiYXNlICgqcmVsZWFzZSAxMzgqKV0oaHR0cHM6Ly9hY2FkZW1pYy5vdXAuY29tL25hci9hcnRpY2xlLzQxL0QxL0Q1OTAvMTA2OTI3Nyl7dGFyZ2V0PSJfYmxhbmsifSAoUXVhc3QgZXQgYWwuLCAyMDEzKS4gQSBbcGh5bG9zZXFdKGh0dHBzOi8vam91cm5hbHMucGxvcy5vcmcvcGxvc29uZS9hcnRpY2xlP2lkPTEwLjEzNzEvam91cm5hbC5wb25lLjAwNjEyMTcpe3RhcmdldD0iX2JsYW5rIn0gb2JqZWN0IChNY011cmRpZSAmIEhvbG1lcywgMjAxMykgaXMgZ2VuZXJhdGVkIGZvciBmdXJ0aGVyIHN0YXRpc3RpY2FsIGFuYWx5c2VzLgoKIyMjICBEQURBMiBwcm9jZXNzCgpGaXJzdCwgeW91IG5lZWQgdG8gbG9hZCB0aGUgb3JpZ2luYWwgUjEgUjIgcmVhZHMgZm9sZGVyIGluIHRoZSAqKi9kaXJfZGF0YXNldC9kaXJfZmFzdHFfc291cmNlKiogZm9sZGVyLiBUaGVuLCBkb3dubG9hZCB0aGUgW1NJTFZBIGRhdGFiYXNlIHJlZmVyZW5jZXNdKGh0dHBzOi8vemVub2RvLm9yZy9yZWNvcmQvMTE3Mjc4MyMuWHBTQjI1TXpaUUkpIGludG8gdGhlICoqL2Rpcl9kYXRhc2V0L2Rpcl9yZWZfZGIqKiBmb2xkZXIuIAoKSW4gdGhpcyBzdHVkeSwgIHdlIHVzZWQgdGhlIHByaW1lcnMgc2V0ICoqMzQzRiAtIDc5NFIqKiBzcGVjaWZpYyBmb3IgdGhlIHJlZ2lvbiBWM1Y0IG9mIHRoZSBwcm9rYXJ5b3RpYyAxNlMgckROQSAoW011eXplciwgMTk5M10oaHR0cHM6Ly9kb2k6MTAuMTEyOC9hZW0uNTkuMy42OTUtNzAwLjE5OTMpKS4gCmBgYHtyIHByaW1lcnMgZGVzY3JpcHRpb24sIGV2YWwgPSBGfQpwcmltZXJfRl8zNDMgPSAiQUNHR1JBR0dDQUdDQUciCnByaW1lcl9SXzc4NCA9ICJUQUNDQUdHR1RBVENUQUFUQyIKYGBgCgpSZWFkIHRoZSBmb2xkZXJzIHRvIGJlIHN1cmUgYWxsIGZpbGVzIGFyZSBpbiBpdCBhbmQgdGhhdCBkaXJlY3Rpb24gaXMgd2VsbCBpbmRpY2F0ZWQuCkdldCB0aGUgbGlzdCBvZiB5b3VyIHNhbXBsZXMgYW5kIGV4dHJhY3QgdGhlIHNhbXBsZSBuYW1lczogCmBgYHtyIHJlZl9mb2xkZXIsIGV2YWw9IEZ9Cm5tc19zZXFfcnVucyAgPC0gbGlzdC5maWxlcyhkaXJfZmFzdHFfc291cmNlKQpwYXRoc19zZXFfcnVucyA8LSBsaXN0LmZpbGVzKGRpcl9mYXN0cV9zb3VyY2UsIGZ1bGwubmFtZXMgPSBUUlVFKSAlPiUgc2V0TmFtZXMobm1zX3NlcV9ydW5zKQoKbm1zX3JlZmRiICAgPC0gbGlzdC5maWxlcyhkaXJfcmVmZGIpCnBhdGhzX3JlZmRiIDwtIGxpc3QuZmlsZXMoZGlyX3JlZmRiLCBmdWxsLm5hbWVzID0gVFJVRSkgJT4lIHNldE5hbWVzKG5tc19yZWZkYikKCmZucyA8LSBzb3J0KHBhdGhzX3NlcV9ydW5zKQpmbnMgPC0gZm5zW3N0cl9kZXRlY3QoYmFzZW5hbWUoZm5zKSwgIi5mYXN0cSIpXQpmbnNfUjEgPC0gZm5zW3N0cl9kZXRlY3QoYmFzZW5hbWUoZm5zKSwgIlIxXzAwMS5mYXN0cSIpXQpmbnNfUjIgPC0gZm5zW3N0cl9kZXRlY3QoYmFzZW5hbWUoZm5zKSwgIlIyXzAwMS5mYXN0cSIpXQppZihsZW5ndGgoZm5zX1IxKSAhPSBsZW5ndGgoZm5zX1IyKSkgc3RvcCgiRm9yd2FyZCBhbmQgcmV2ZXJzZSBmaWxlcyBkbyBub3QgbWF0Y2guIikKCnNhbXBsZV9uYW1lcyA8LSBzYXBwbHkoc3Ryc3BsaXQoYmFzZW5hbWUoZm5zX1IxKSwgIl8iKSwgYFtgLCAxKQpzYW1wbGVfbmFtZXMKYGBgCgpPbmNlIHNhbXBsZSBuYW1lcyBjdXQsIHlvdSBjYW4gcmVtb3ZlIHRoZSBwcmltZXIgc2VxdWVuY2UgZnJvbSB5b3VyIGRhdGEgYnkgImNvdW50aW5nIiB0aGUgbnVtYmVyIG9mIG51Y2xlb3RpZGVzIG9mIGVhY2ggcHJpbWVyLiBCZWNhdXNlIG51Y2xlb3RpZGVzIG1heWJlIG1lIGZsb3dpbmcsIGl0IG5vdCBhZHZpc2VkIHRvIHJlbW92ZSB0aGUgc2VxdWVuY2Ugb2YgdGhlIHByaW1lciBieSBpdHMgbnVjbGVvdGlkZSBjb21wb3NpdGlvbi4KCmBgYHtyIHJlbW92ZV9wcmltZXJfc2VxICwgZXZhbCA9IEZ9CnByaW1lcl9sZW5ndGhfZndkID0gc3RyX2xlbmd0aChwcmltZXJfRl8zNDMpCnByaW1lcl9sZW5ndGhfcmV2ID0gc3RyX2xlbmd0aChwcmltZXJfUl83ODQpCmBgYAoKIyMjIyBRdWFsaXR5IHByb2ZpbGVzIGFuZCBmaWx0ZXIvdHJpbSB0aGUgc2VxdWVuY2VzCk5vdyB5b3UgY2FuIHBsb3QgdGhlIHF1YWxpdHkgcHJvZmlsZXMgb2YgeW91ciByZWFkcyAod2hpY2ggaXMgdXNlbHkgYmV0dGVyIG9uIHRoZSBSMSkgaW4gb3JkZXIgdG8gdHJ1bmNhdGUgdGhlIHNlcXVlbmNlcyBiZWZvcmUgcXVhbGl0eSBkcmFzdGljYWxseSBkZWNyZWFzZS4gVGhlbiwgeW91IGNhbiBmaWx0ZXIgeW91ciBzZXF1ZW5jZXMgYW5kIHRyaW0gdGhlIHByaW1lciBsZW5ndGguIAoKYGBge3IgUXVhbGl0eSBwcm9maWxlcyBhbmQgZmlsdGVyaW5nICwgZXZhbCA9IEZ9CnF1YWxfUjEgPC0gcGxvdFF1YWxpdHlQcm9maWxlKGZuc19SMVsxXSkKcXVhbF9SMiA8LSBwbG90UXVhbGl0eVByb2ZpbGUoZm5zX1IyWzFdKQpnZ3NhdmUocXVhbF9SMSwgZmlsZSA9IHBhc3RlMChkaXJfZ3JhcGggLCAicXVhbGl0eV9wcm9maWxlX1IxLnBuZyIpKQpnZ3NhdmUocXVhbF9SMiwgZmlsZSA9IHBhc3RlMChkaXJfZ3JhcGgsICJxdWFsaXR5X3Byb2ZpbGVfUjIucG5nIikpCmZpbHRfUjEgPC0gc3RyX2MoZGlyX2ZpbHRlcmVkLCBzYW1wbGVfbmFtZXMsICJfUjFfZmlsdC5mYXN0cSIpCmZpbHRfUjIgPC0gc3RyX2MoZGlyX2ZpbHRlcmVkLCBzYW1wbGVfbmFtZXMsICJfUjJfZmlsdC5mYXN0cSIpCm5hbWVzKGZpbHRfUjEpIDwtIHNhbXBsZV9uYW1lcwpuYW1lcyhmaWx0X1IyKSA8LSBzYW1wbGVfbmFtZXMKc2V0LnNlZWQoMTAwMCkKb3V0IDwtIGZpbHRlckFuZFRyaW0oZm5zX1IxLCBmaWx0X1IxLCBmbnNfUjIsIGZpbHRfUjIsIHRydW5jTGVuPWMoMjQwLDI0MCksCiAgICAgICAgICAgICAgICAgICAgIG1heE49MCwgbWF4RUU9YygyLDIpLCB0cnVuY1E9MiwgdHJpbUxlZnQ9YyhwcmltZXJfbGVuZ3RoX2Z3ZCxwcmltZXJfbGVuZ3RoX3JldikgLCBybS5waGl4PVRSVUUsCiAgICAgICAgICAgICAgICAgICAgIGNvbXByZXNzPVRSVUUsIG11bHRpdGhyZWFkPVRSVUUpCmhlYWQob3V0KQoKc2FtcGxlX25hbWVzIDwtIHNhcHBseShzdHJzcGxpdChiYXNlbmFtZShmaWx0X1IxKSwgIl8iKSwgYFtgLCAxKSAjIEFzc3VtZXMgZmlsZW5hbWUgPSBzYW1wbGVuYW1lX1hYWC5mYXN0cS5negpzYW1wbGVfbmFtZXNSIDwtIHNhcHBseShzdHJzcGxpdChiYXNlbmFtZShmaWx0X1IyKSwgIl8iKSwgYFtgLCAxKSAjIEFzc3VtZXMgZmlsZW5hbWUgPSBzYW1wbGVuYW1lX1hYWC5mYXN0cS5negppZighaWRlbnRpY2FsKHNhbXBsZV9uYW1lcywgc2FtcGxlX25hbWVzUikpIHN0b3AoIkZvcndhcmQgYW5kIHJldmVyc2UgZmlsZXMgZG8gbm90IG1hdGNoLiIpCm5hbWVzKGZpbHRfUjEpIDwtIHNhbXBsZV9uYW1lcwpuYW1lcyhmaWx0X1IyKSA8LSBzYW1wbGVfbmFtZXMKc2V0LnNlZWQoMTAwMCkKYGBgCgpGaW5hbGx5LCB5b3UgY2FuIGxlYXJuIHRoZSBlcnJvciByYXRlcyBmb3IgYm90aCByZWFkcyBhbmQgc2F2ZSB0aGVtLiAKYGBge3IgTGVhcm4gdGhlIGVycm9yIHJhdGUsICBldmFsID0gRn0KZXJyRiA8LSBsZWFybkVycm9ycyhmaWx0X1IxLG5iYXNlcz0xZTgsIG11bHRpdGhyZWFkPVRSVUUpCmVyclIgPC0gbGVhcm5FcnJvcnMoZmlsdF9SMiwgbmJhc2VzPTFlOCwgbXVsdGl0aHJlYWQ9VFJVRSkKcGxvdEVycm9yc19GIDwtIHBsb3RFcnJvcnMoZXJyRiwgbm9taW5hbFE9VFJVRSkKcGxvdEVycm9yc19SIDwtIHBsb3RFcnJvcnMoZXJyUiwgbm9taW5hbFE9VFJVRSkKZ2dzYXZlKHBsb3RFcnJvcnNfRiwgZmlsZSA9IHBhc3RlMChkaXJfZ3JhcGggLCAicGxvdEVycm9yc19GLnBuZyIpKQpnZ3NhdmUocGxvdEVycm9yc19SLCBmaWxlID0gcGFzdGUwKGRpcl9ncmFwaCwgInBsb3RFcnJvcnNfUi5wbmciKSkKYGBgCgojIyMjIERlcmVwbGljYXRpb24gYW5kIG1lcmdpbmcgc2VxdWVuY2VzCk5vdywgd2UgZGVyZXBsaWNhdGUgdGhlIGZpbHRlcmVkIHJlYWRzIGluIG9yZGVyIHRvIGluZmVyIHRoZW0gaW4gZGFkYSBmaWxlIHRvIG1lcmdlLgoKYGBge3IgRGVyZXBsaWNhdGlvbiAmIEluZmVyZW5jZSAsIGV2YWwgPSBGfQptZXJnZXJzIDwtIHZlY3RvcigibGlzdCIsIGxlbmd0aChzYW1wbGVfbmFtZXMpKQpuYW1lcyhtZXJnZXJzKSA8LSBzYW1wbGVfbmFtZXMKZm9yKHNhbSBpbiBzYW1wbGVfbmFtZXMpIHsKICBjYXQoIlByb2Nlc3Npbmc6Iiwgc2FtLCAiXG4iKQogIGRlcmVwRnMgPC0gZGVyZXBGYXN0cShmaWx0X1IxW1tzYW1dXSkKICBkZEYgPC0gZGFkYShkZXJlcEZzLCBlcnI9ZXJyRiwgbXVsdGl0aHJlYWQ9VFJVRSkKICBkZXJlcFJzIDwtIGRlcmVwRmFzdHEoZmlsdF9SMltbc2FtXV0pCiAgZGRSIDwtIGRhZGEoZGVyZXBScywgZXJyPWVyclIsIG11bHRpdGhyZWFkPVRSVUUpCiAgbWVyZ2VyIDwtIG1lcmdlUGFpcnMoZGRGLCBkZXJlcEZzLCBkZFIsIGRlcmVwUnMpCiAgbWVyZ2Vyc1tbc2FtXV0gPC0gbWVyZ2VyCn0KCnJtKGRlcmVwRnMpOyBybShkZXJlcFJzKQpgYGAKCk5vdywgd2UgY2FuIGNvbnN0cnVjdCBvdXIgc2VxdWVuY2UgdGFibGUgInNlcXRhYiIgYW5kIHJlbW92ZSBjaGltZXJhcy4gSXQgaXMgcmVjb21tYW5kYWJsZSB0byB0cmFjayB0aGUgcmVhZHMgdGhyb3VnaCB0aGUgcGlwZWxpbmUgcHJvY2VzcwpgYGB7ciBjb25zdHJ1Y3Qgc2VxdWVuY2UgdGFibGUgJiBjaGltZXJhcyByZW1vdmluZywgZXZhbCA9IEZ9CnNlcXRhYiA8LSBtYWtlU2VxdWVuY2VUYWJsZShtZXJnZXJzKQpyb3cubmFtZXMoc2VxdGFiKSA9IHNhbXBsZV9uYW1lcwpkaW0oc2VxdGFiKQp0YWJsZShuY2hhcihnZXRTZXF1ZW5jZXMoc2VxdGFiKSkpCgpzZXF0YWIubm9jaGltIDwtIHJlbW92ZUJpbWVyYURlbm92byhzZXF0YWIsIG1ldGhvZD0iY29uc2Vuc3VzIiwgbXVsdGl0aHJlYWQ9VFJVRSwgdmVyYm9zZT1UUlVFKQpkaW0oc2VxdGFiLm5vY2hpbSkKc3VtKHNlcXRhYi5ub2NoaW0pL3N1bShzZXF0YWIpCnNhdmVSRFMoc2VxdGFiLm5vY2hpbSwgcGFzdGUwKGRpcl9maWx0ZXJlZCAsInNlcXRhYi5ub2NoaW0ucmRzIikpCgojIF9fX18gVHJhY2sgcmVhZHMgdGhyb3VnaCB0aGUgcGlwZWxpbmUgLS0tLQpnZXROIDwtIGZ1bmN0aW9uKHgpIHN1bShnZXRVbmlxdWVzKHgpKQp0cmFjayA8LSBjYmluZChvdXQsIHNhcHBseShkYWRhRnMsIGdldE4pLCBzYXBwbHkobWVyZ2VycywgZ2V0TiksIHJvd1N1bXMoc2VxdGFiKSwgcm93U3VtcyhzZXF0YWIubm9jaGltKSkKY29sbmFtZXModHJhY2spIDwtIGMoImlucHV0IiwgImZpbHRlcmVkIiwgImRlbm9pc2VkIiwgIm1lcmdlZCIsICJ0YWJsZWQiLCAibm9uY2hpbSIpCnJvd25hbWVzKHRyYWNrKSA8LSBzYW1wbGVfbmFtZXMKaGVhZCh0cmFjaykKc2F2ZSh0cmFjaywgZmlsZSA9IHBhc3RlMChkaXJfZmlsdGVyZWQsICJ0cmFja18zNDNGLTc4NFIuUkRhdGEiKQpgYGAKCioqKgojIyMjIFRheG9ub215IGFzc2lnbm1lbnQKCkZyb20gdGhlIFNJTFZBIHJlZiBmaWxlcyBwcmV2aW91c2x5IGRlcG9zaXRlZCBpbiB0aGUgKipkaXJfcmVmZGIqKiBmb2xkZXIsIGFzc2lnbiB0aGUgc2VxdWVuY2UgdGFibGVzCmBgYHtyIFRheCBhc3NpZ25tZW50LCBldmFsID0gRn0KcGF0aF9yZWZlcmVuY2VfZGIgPC0gcGFzdGUwKGRpcl9yZWZkYiwgIi9zaWx2YV9ucl92MTM4X3RyYWluX3NldC5mYS5neiIpCnBhdGhfc3BlY2llc19kYiAgIDwtIHBhc3RlMChkaXJfcmVmZGIsICIvc2lsdmFfc3BlY2llc19hc3NpZ25tZW50X3YxMzguZmEiKQp0YXhhUkMgPC0gYXNzaWduVGF4b25vbXkoc2VxdGFiLm5vY2hpbSwgcGF0aF9yZWZlcmVuY2VfZGIsIHRyeVJDPVRSVUUpCnRheGFTcCA8LSBhZGRTcGVjaWVzKHRheGFSQywgcGF0aF9zcGVjaWVzX2RiKSAjIFRoaXMgc3RlcCBjYW4gYmUgdmVyeSBsb25nCgp0YXhhLnByaW50IDwtIHRheGFTcCAjIFJlbW92aW5nIHNlcXVlbmNlIHJvd25hbWVzIGZvciBkaXNwbGF5IG9ubHkKcm93bmFtZXModGF4YS5wcmludCkgPC0gTlVMTApoZWFkKHRheGEucHJpbnQpCmBgYAoKIyMjIFBoeWxvc2VxIGNyZWF0aW9uCk5vdyB5b3UgY2FuIGNyZWF0ZSB5b3VyIHBoeWxvc2VxIG9iamVjdCB3aXRoIHlvdXIgZmlsdGVyZWQgYW5kIGFzc2lnbmVkIHNlcXVlbmNlcy4gCgpgYGB7ciBwaHlsb3NlcV9jcmVhdGlvbiAsIGV2YWw9IEZ9CnBzIDwtIHBoeWxvc2VxKG90dV90YWJsZShzZXF0YWIubm9jaGltLCB0YXhhX2FyZV9yb3dzPUZBTFNFKSx0YXhfdGFibGUodGF4YVNwKSkgCmRuYSA8LSBCaW9zdHJpbmdzOjpETkFTdHJpbmdTZXQodGF4YV9uYW1lcyhwcykpCm5hbWVzKGRuYSkgPC0gdGF4YV9uYW1lcyhwcykKcHMgPC0gbWVyZ2VfcGh5bG9zZXEocHMsIGRuYSkKdGF4YV9uYW1lcyhwcykgPC0gcGFzdGUwKCJBU1YiLCBzZXEobnRheGEocHMpKSkKcHMKc2F2ZShwcywgZmlsZSA9IHBhc3RlMChkaXJfZmlsdGVyZWQsICJwcy5SRGF0YSIpKQpgYGAKCj4gRnJlZSByIHN0YWNrIG1lbW9yeSBpbiBvcmRlciB0byBwdXJzdWUgdGhlIGFuYWx5c2lzLiBEb24ndCBmb3JnZXQgdG8gcmVsb2FkIGFsbCB0aGUgbGlicmFyeSBwYWNrYWdlcyBhbmQgcmVhc3NpZ24gdGhlIHBhdGh3YXlzCgojIyMjIEVudmlyb25tZW50YWwgZGF0YSB0YWJsZSBtZXJnaW5nCgpUaGUgZW52ZGF0YSBpcyBjb25zdGl0dXRlZCBvZiB0aGUgc2FtcGxpbmcgZGF0YSBmcm9tIHRoZSAqKkFTX2RhdGEqKiBwcmV2aW91c2x5IGNyZWF0ZWQuClRoaXMgc3RlcCBpcyBuZWNlc3NhcnkgdG8gaG9tb2dlbml6ZSB0aGUgbmFtZXMgb2YgdGhlIHBoeWxvc2VxIG9iamVjdCB3aXRoIHRoZSBlbnZkYXRhLgpGaXJzdCwgY2hhbmdlIHRoZSBJRCBuYW1lcyB0byBiZSBjb25ncnVlbnQgd2l0aCB0aGUgbmFtZXMgb2YgdGhlIHNlcXVlbmNlcy4KCmBgYHtyIE9yZGVyIHRoZSBuYW1lcyAsIGV2YWwgPSBGfQpBU19kYXRhIDwtIHJlYWQudGFibGUoZmlsZSA9IHBhc3RlMChkaXJfZGF0YXNldCwgIkFTX2RhdGEudHh0IiksIHNlcCA9Ilx0IiwgaGVhZGVyID0gVCkKQVNfZGF0YSRJRCA8LSBzdHJfcmVwbGFjZV9hbGwoQVNfZGF0YSRJRCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoIk1TLiIgPSAiTVMtIiwiTUMuIiA9ICJNQy0iKSkKQVNfZGF0YSRJRCA8LSBwYXN0ZTAoQVNfZGF0YSRJRCAsICItRyIpCgplbnZfZGF0IDwtIEFTX2RhdGEgJT4lIGZpbHRlcihJRCAlaW4lIHNhbXBsZV9uYW1lcyhwcykpCmVudl9kYXQgPC0gc2FtcGxlX2RhdGEoZW52X2RhdCkKc2FtcGxlX25hbWVzKGVudl9kYXQpIDwtIGVudl9kYXQkSUQKCnNvcnQoc2FtcGxlX25hbWVzKGVudl9kYXQpKSA9PSBzb3J0KHNhbXBsZV9uYW1lcyhwcykpCmBgYAoKTm93IHRoYXQgdGhlIHNlcXRhYiBhbmQgdGhlIGVudmRhdGEgYXJlIGNvcnJlc3BvbmRpbmcsbWVyZ2UgdGhlbSBpbnRvIHBoeWxvc2VxIG9iamVjdC4KCmBgYHtyIE1lcmdlIGVudmRhdGEgYW5kIHBoeWxvc2VxLCBldmFsPUZ9CnBzMSA8LSBtZXJnZV9waHlsb3NlcShwcywgZW52X2RhdCkKcHMxCnNhdmUocHMxLCBmaWxlID0gcGFzdGUwKGRpcl9maWx0ZXJlZCwgInBzMS5SRGF0YSIpKQpgYGAKClBsb3QgdGhlIHNhbXBsZSBtaW5pbXVtIEFTViAKYGBge3Igc2FtcGxlIG1pbmltdW0gQVNWICwgZXZhbCA9IFQsIGZpZy5hbGlnbj0nY2VudGVyJ30KbG9hZChmaWxlID0gcGFzdGUwKGRpcl9maWx0ZXJlZCwgInBzMS5SRGF0YSIpKQoKcGFzdGUwKCJUaGUgbWluaW11bSBzYW1wbGUgc3VtIG9mIHRoZSBkYXRhc2V0IGlzICIsbWluKHJvd1N1bXMocHMxQG90dV90YWJsZUAuRGF0YSkpKQpyZWFkc3Vtc2RmID0gZGF0YS5mcmFtZShucmVhZHMgPSBzb3J0KHRheGFfc3VtcyhwczEpLFRSVUUpLCBzb3J0ZWQgPSAxOm50YXhhKHBzMSksIHR5cGUgPSAiQVNWcyIpCnJlYWRzdW1zZGYgPSByYmluZChyZWFkc3Vtc2RmLCBkYXRhLmZyYW1lKG5yZWFkcyA9IHNvcnQoc2FtcGxlX3N1bXMocHMxKSwgVFJVRSksIHNvcnRlZCA9IDE6bnNhbXBsZXMocHMxKSwgdHlwZSA9ICJTYW1wbGVzIikpCnAgPSBnZ3Bsb3QocmVhZHN1bXNkZiwgYWVzKHggPSBzb3J0ZWQsIHkgPSBucmVhZHMpKSArIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKQpwICsgZ2d0aXRsZSgiVG90YWwgbnVtYmVyIG9mIHJlYWRzIikgKyBzY2FsZV95X2xvZzEwKCkgKyBmYWNldF93cmFwKH50eXBlLCAxLCBzY2FsZXMgPSAiZnJlZSIpCmBgYAoKIyMjIE5vcm1hbGl6YXRpb24gYW5kIGNhbGlicmF0aW9uCgpGaWx0ZXIgdGhlIHBzMSB0byBvbmx5IGtlZXAgdGhlICoqUHJva2FyeW90ZXMqKgoKYGBge3IgUHJva2FyeW90ZXNfc2VsZWN0aW9uLCBldmFsID0gRn0KcHNfcHJva2EgPC0gIHN1YnNldF90YXhhKHBzMSwgS2luZ2RvbSAlaW4lIGMoIkFyY2hhZWEiLCAiQmFjdGVyaWEiKSAmIAogICAgICAgICAgICAgICAgICAgICAgICAgICBPcmRlciAhPSAiQ2hsb3JvcGxhc3QiICYgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIEZhbWlseSAhPSAiTWl0b2Nob25kcmlhIikKc2F2ZShwc19wcm9rYSwgZmlsZSA9IHBhc3RlMChkaXJfZmlsdGVyZWQsInBzX3Byb2thLlJEYXRhIikpCmBgYAoKTm9ybWFsaXphdGlvbiBvZiBhbGwgc2FtcGxlcyBhdCAxMCAxODAgc2VxdWVuY2VzICgqaS5lLiwqIHRoZSBtaW5pbXVtIHNhbXBsZSBzdW0gb2YgdGhlIGRhdGFzZXQpIGZvciBhIGZpbmFsIGRhdGFzZXQgY29uc3RpdHV0ZWQgYnkgMzk3IDAyMCBzZXF1ZW5jZXMuCgoKCj4gRWFjaCB0aW1lIHRoZSBmdW5jdGlvbiAqcmFyZWZ5X2V2ZW5fZGVwdGgoKSogaXMgcmFuLCBpdCByYW5kb21seSBjcmVhdGVzIGEgbmV3IHJhcmVmaWVzIHBoeWxvc2VxIHdpdGggdGhlIHNlcXVlbmNlcyBlbnRlcmVkIGluIHRoZSBmdW5jdGlvbi4gVGhhdCBtZWFucyB0aGF0IHdpdGggdGhlIHNhbWUgZnVuY3Rpb24gYW5kIGFyZ3VtZW50LCB0aGUgKnBzX3JmZiogd2lsbCBiZSB3aXRoIHRoZSBzYW1lIGxlbmd0aCAoc2FtZSBudW1iZXIgb2Ygc2VxdWVuY2VzKSBidXQgbWlnaHQgYmUgZGlmZmVyZW50IGluIHRlcm0gb2YgcXVhbGl0eSBvZiBzZXF1ZW5jZXMgKG5vdCB0aGUgc2FtZSBudW1iZXIgb2YgQVNWcykuCgpgYGB7ciBub3JtYWxpemF0aW9uIGZ1bmN0aW9uLCBldmFsID0gVCwgd2FybmluZz1GQUxTRSwgbWVzc2FnZSA9IEZ9CmxvYWQoZmlsZSA9IHBhc3RlMChkaXJfZmlsdGVyZWQsInBzX3Byb2thLlJEYXRhIikpCnBhc3RlMCgiVGhlIG1pbmltdW0gc2FtcGxlIHN1bSBmb3IgdGhlIHByb2thcnlvdGUgc2VxdWVuY2VzIGlzICIsbWluKHJvd1N1bXMocHNfcHJva2FAb3R1X3RhYmxlQC5EYXRhKSkpCnBzX3JmZiA8LSBwcnVuZV9zYW1wbGVzKHNhbXBsZV9zdW1zKHBzX3Byb2thKSA+PSBtaW4oc2FtcGxlX3N1bXMocHNfcHJva2EpKSAsIHBzX3Byb2thKSAjIG5vcm1hbGl6ZWQgYXQgMTAxODAgc2VxL3NhbXBsZQpwc19yZmYgPC0gcmFyZWZ5X2V2ZW5fZGVwdGgocHNfcHJva2EsIHNhbXBsZS5zaXplID0gbWluKHNhbXBsZV9zdW1zKHBzX3Byb2thKSkpCmNvdl9yZmYgPC0gZ29vZHMocHNfcmZmQG90dV90YWJsZUAuRGF0YSkKcGFzdGUwKCJUaGUgY292ZXJhZ2UgYWZ0ZXIgbm9ybWFsaXphdGlvbiBpcyAiLCByb3VuZChtZWFuKGNvdl9yZmYkZ29vZHMpLDIpLCIlICstICIgLHJvdW5kKHNkKGNvdl9yZmYkZ29vZHMpLDIpKQpwYXN0ZTAoIlRoZSByYXRpbyBiZXR3ZWVuIHRoZSBpbml0aWFsIHNldCBhbmQgdGhlIG5vcm1hbGl6ZWQgZGF0YSAiLHJvdW5kKHRheGFfc3Vtcyhwc19yZmYpICU+JSBuYW1lcygpICU+JSBsZW5ndGgoKSAvIHRheGFfc3Vtcyhwc19wcm9rYSkgJT4lIG5hbWVzKCkgJT4lIGxlbmd0aCgpLCAyKSwgIiUiKQp0YXhfdGFibGUocHNfcmZmKSA8LSBjYmluZCh0YXhfdGFibGUocHNfcmZmKSwiQVNWIj0gdGF4X3RhYmxlKHBzX3JmZikpICMgQWRkIGEgbmV3IGNvbG9tdW4gd2l0aCB0aGUgQVNWIElkCiNzYXZlKHBzX3JmZiwgZmlsZSA9IHBhc3RlMChkaXJfZGF0YXNldCwicHNfcmZmLlJEYXRhIikpICMgSWYgeW91IHJ1biBhbmQgc2F2ZSB0aGUgcHNfcmZmIGVhY2ggdGltZSwgeW91ciBmaWxlIHdpbGwgY2hhbmdlIGFuZCB0aGUgZnVydGhlciBhbmFseXNlcyB0b28gKGV2ZW4gaWYgc2xpZ2h0bHkpLiAgCmBgYAoKPiBUbyBiZSBzdXJlIHRoYXQgdGhlIG1pbmltYWwgYWJ1bmRhbmNlIG9mIHRoZSBkYXRhIHNldCBpcyBlbm91Z2ggdG8gY2FwdHVyZSBhbGwgdGhlIGRpdmVyc2l0eSwgd2UgdXNlIHRoZSBmdW5jdGlvbiAqKmdncmFyZSgpKiogCgpgYGB7ciBnZ3JhcmUgZnVuY3Rpb24sIGV2YWwgPSBUfQpnZ3JhcmUgPC0gZnVuY3Rpb24ocGh5c2VxX29iamVjdCwgc3RlcCA9IDEwLCBsYWJlbCA9IE5VTEwsIGNvbG9yID0gTlVMTCwgcGxvdCA9IFRSVUUsIHBhcmFsbGVsID0gRkFMU0UsIHNlID0gVFJVRSkgewogIHggPC0gbWV0aG9kczo6YXMocGh5bG9zZXE6Om90dV90YWJsZShwaHlzZXFfb2JqZWN0KSwgIm1hdHJpeCIpCiAgaWYgKHBoeWxvc2VxOjp0YXhhX2FyZV9yb3dzKHBoeXNlcV9vYmplY3QpKSB7IHggPC0gdCh4KSB9CiMjIFRoaXMgc2NyaXB0IGlzIGFkYXB0ZWQgZnJvbSB2ZWdhbiBgcmFyZWN1cnZlYCBmdW5jdGlvbgogIHRvdCA8LSByb3dTdW1zKHgpCiAgUyA8LSByb3dTdW1zKHggPiAwKQogIG5yIDwtIG5yb3coeCkKICByYXJlZnVuIDwtIGZ1bmN0aW9uKGkpIHsKICAgIGNhdChwYXN0ZSgicmFyZWZ5aW5nIHNhbXBsZSIsIHJvd25hbWVzKHgpW2ldKSwgc2VwID0gIlxuIikKICAgIG4gPC0gc2VxKDEsIHRvdFtpXSwgYnkgPSBzdGVwKQogICAgaWYgKG5bbGVuZ3RoKG4pXSAhPSB0b3RbaV0pIHsKICAgICAgbiA8LSBjKG4sIHRvdFtpXSkKICAgIH0KICAgIHkgPC0gdmVnYW46OnJhcmVmeSh4W2ksICxkcm9wID0gRkFMU0VdLCBuLCBzZSA9IHNlKQogICAgaWYgKG5yb3coeSkgIT0gMSkgewogICAgICByb3duYW1lcyh5KSA8LSBjKCIuUyIsICIuc2UiKQogICAgICByZXR1cm4oZGF0YS5mcmFtZSh0KHkpLCBTaXplID0gbiwgU2FtcGxlID0gcm93bmFtZXMoeClbaV0pKQogICAgfSBlbHNlIHsKICAgICAgcmV0dXJuKGRhdGEuZnJhbWUoLlMgPSB5WzEsIF0sIFNpemUgPSBuLCBTYW1wbGUgPSByb3duYW1lcyh4KVtpXSkpCiAgICB9CiAgfQogIGlmIChwYXJhbGxlbCkgewogICAgb3V0IDwtIHBhcmFsbGVsOjptY2xhcHBseShzZXFfbGVuKG5yKSwgcmFyZWZ1biwgbWMucHJlc2NoZWR1bGUgPSBGQUxTRSkKICB9IGVsc2UgewogICAgb3V0IDwtIGxhcHBseShzZXFfbGVuKG5yKSwgcmFyZWZ1bikKICB9CiAgZGYgPC0gZG8uY2FsbChyYmluZCwgb3V0KQogICMgR2V0IHNhbXBsZSBkYXRhCiAgaWYgKCFpcy5udWxsKHBoeWxvc2VxOjpzYW1wbGVfZGF0YShwaHlzZXFfb2JqZWN0LCBGQUxTRSkpKSB7CiAgICBzZGYgPC0gbWV0aG9kczo6YXMocGh5bG9zZXE6OnNhbXBsZV9kYXRhKHBoeXNlcV9vYmplY3QpLCAiZGF0YS5mcmFtZSIpCiAgICBzZGYkU2FtcGxlIDwtIHJvd25hbWVzKHNkZikKICAgIGRhdGEgPC0gbWVyZ2UoZGYsIHNkZiwgYnkgPSAiU2FtcGxlIikKICAgIGxhYmVscyA8LSBkYXRhLmZyYW1lKHggPSB0b3QsIHkgPSBTLCBTYW1wbGUgPSByb3duYW1lcyh4KSkKICAgIGxhYmVscyA8LSBtZXJnZShsYWJlbHMsIHNkZiwgYnkgPSAiU2FtcGxlIikKICB9CiAgIyBBZGQsIGFueSBjdXN0b20tc3VwcGxpZWQgcGxvdC1tYXBwZWQgdmFyaWFibGVzCiAgaWYgKCBsZW5ndGgoY29sb3IpID4gMSApIHsKICAgIGRhdGEkY29sb3IgPC0gY29sb3IKICAgIG5hbWVzKGRhdGEpW25hbWVzKGRhdGEpID09ICJjb2xvciJdIDwtIGRlcGFyc2Uoc3Vic3RpdHV0ZShjb2xvcikpCiAgICBjb2xvciA8LSBkZXBhcnNlKHN1YnN0aXR1dGUoY29sb3IpKQogIH0KICBpZiAoIGxlbmd0aChsYWJlbCkgPiAxICkgewogICAgbGFiZWxzJGxhYmVsIDwtIGxhYmVsCiAgICBuYW1lcyhsYWJlbHMpW25hbWVzKGxhYmVscykgPT0gImxhYmVsIl0gPC0gZGVwYXJzZShzdWJzdGl0dXRlKGxhYmVsKSkKICAgIGxhYmVsIDwtIGRlcGFyc2Uoc3Vic3RpdHV0ZShsYWJlbCkpCiAgfQogIHAgPC0gZ2dwbG90Mjo6Z2dwbG90KGRhdGEgPSBkYXRhLAogICAgICAgICAgICAgICAgICAgICAgIGdncGxvdDI6OmFlc19zdHJpbmcoeCA9ICJTaXplIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSAiLlMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSAiU2FtcGxlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gY29sb3IpKQogIHAgPC0gcCArIGdncGxvdDI6OmxhYnMoeCA9ICJTZXF1ZW5jZSBTYW1wbGUgU2l6ZSIsIHkgPSAiU3BlY2llcyBSaWNobmVzcyIpCiAgaWYgKCFpcy5udWxsKGxhYmVsKSkgewogICAgcCA8LSBwICsgZ2dwbG90Mjo6Z2VvbV90ZXh0KGRhdGEgPSBsYWJlbHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2dwbG90Mjo6YWVzX3N0cmluZyh4ID0gIngiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9ICJ5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gbGFiZWwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IGNvbG9yKSwKICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gNCwgaGp1c3QgPSAwKQogIH0KICBwIDwtIHAgKyBnZ3Bsb3QyOjpnZW9tX2xpbmUoKQogIGlmIChzZSkgeyAjIyBhZGQgc3RhbmRhcmQgZXJyb3IgaWYgYXZhaWxhYmxlCiAgICBwIDwtIHAgKwogICAgICBnZ3Bsb3QyOjpnZW9tX3JpYmJvbihnZ3Bsb3QyOjphZXNfc3RyaW5nKHltaW4gPSAiLlMgLSAuc2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHltYXggPSAiLlMgKyAuc2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gTlVMTCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gY29sb3IpLAogICAgICAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDAuMikKICB9CiAgaWYgKHBsb3QpIHsKICAgIHBsb3QocCkKICB9CiAgaW52aXNpYmxlKHApCn0KYGBgCgpgYGB7ciBnZ3JhcmUgY3VydmUsIGV2YWw9VCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgcmVzdWx0cz0naGlkZScsIGZpZy5oZWlnaHQ9IDUsIGZpZy53aWR0aD0xMH0KbG9hZChmaWxlID0gcGFzdGUwKGRpcl9kYXRhc2V0LCJwc19yZmYuUkRhdGEiKSkKZ2dyYXJlKHBzX3JmZiwgc3RlcCA9IDUwMCwgIGxhYmVsID0gIklEIiwgc2UgPSBGLCBjb2xvciA9ICJzdGF0dXMiLCBwbG90ID0gRikgKyAKICB0aGVtZV9idygpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiZGFya2JsdWUiICwgImRhcmtyZWQiKSwgCiAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCdMb3ctYWdncmVnYXRpb24nLCAnSGlnaC1hZ2dyZWdhdGlvbicpKSArCiAgdGhlbWUoYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiLCBzaXplID0gMTIpLCAKICAgICAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gInNlcmlmIiksIAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJzZXJpZiIsc2l6ZSA9IDEyKSwgCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gInNlcmlmIixzaXplID0gMTIpLCAKICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoc2l6ZSA9IDApLCAKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gInNlcmlmIiksCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCAgZmFtaWx5ID0gInNlcmlmIiksIAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhbWlseSA9ICJzZXJpZiIpKSsKICBsYWJzKGNvbG91ciA9ICJBZ2dyZWdhdGlvbiBsZXZlbHMiKQpgYGAKCiMjIyMgUGh5bG9nZW5ldGljIGNhbGlicmF0aW9uIC0tLS0KCkJlY2F1c2Ugd2UgdXNlZCBwaHlsb2dlbmV0aWMgaW5kaWNlcyB0aGF0IHRha2UgaW50byBhY2NvdW50IHRoZSBwaHlsb2dlbmV0aWMgZGlzdGFuY2UgYmV0d2VlbiB0YXhhLCB3ZSBuZWVkIHRvIGNhbGlicmF0ZSBhbmQgaW5zZXJ0IHRoZSBwaGh5bG9nZW5ldGljIHRyZWUgb2YgdGhlIHBoeWxvc2VxIG9iamVjdC4gVG8gZG8gdGhhdCwgY3JlYXRlIHRoZSAqKnBzX3JmZi5mYXN0YSoqIChhIG5vdGUgZmlsZSB0aGF0IGNvbnRhaW5zIGFsbCB0aGUgQVNWcyB3aXRoIHRoZWlyIHN1Y2Nlc3NpdmUgbnVjbGVvdGlkaWMgc2VxdWVuY2VzKS4KCmBgYHtyIHBoeWxvZ2VuZXRpYyBjYWxpYnJhdGlvbiwgZXZhbCA9IEZ9CkJpb3N0cmluZ3M6OndyaXRlWFN0cmluZ1NldChwc19yZmZAcmVmc2VxLCBmaWxlID0gInBzX3JmZi5mYXN0YSIpICNjcmVhdGUgZmFzdGEgZmlsZQpgYGAKCllvdSBhbHNvIG5lZWQgdGhlICoqcHNfcmZmLmJpb20qKiBmaWxlIHdoaWNoIGlzIHRoZSBvdXRwdXQgZm9ybWF0IHdoZW4geW91IHByb2NlZWQgeW91ciBsaWJyYWlyaWVzIGluIHRoZSBRaWltZTIgcGlwZWxpbmUuCgo+IEhlcmUgaXMgYSBmdW5jdGlvbiB0aGF0IGFsbG93cyB0byB0cmFuc2Zvcm0gdGhlIG90dSBtYXRyaXggaW4gYSBiaW9tIGZpbGUuCmBgYHtyIGJpb21lIGZ1biwgZXZhbCA9IEZ9Cm1ha2VfYmlvbSA8LSBmdW5jdGlvbihkYXRhLCBzYW1wbGVfbWV0YWRhdGE9TlVMTCwgb2JzZXJ2YXRpb25fbWV0YWRhdGE9TlVMTCwgaWQ9TlVMTCwgbWF0cml4X2VsZW1lbnRfdHlwZT0iaW50Iil7CiAgIyBUaGUgb2JzZXJ2YXRpb25zIC8gZmVhdHVyZXMgLyBPVFVzIC8gcm93cyAibWV0YSIgZGF0YSB0YWJsZQogIGlmKCFpcy5udWxsKG9ic2VydmF0aW9uX21ldGFkYXRhKSl7CiAgICByb3dzID0gbWFwcGx5KGxpc3QsIFNJTVBMSUZZPUZBTFNFLCBpZD1hcy5saXN0KHJvd25hbWVzKGRhdGEpKSwKICAgICAgICAgICAgICAgICAgbWV0YWRhdGE9YWxwbHkoYXMubWF0cml4KG9ic2VydmF0aW9uX21ldGFkYXRhKSwgMSwgLmV4cGFuZD1GQUxTRSwgLmRpbXM9VFJVRSkpCiAgfSBlbHNlIHsKICAgIHJvd3MgPSBtYXBwbHkobGlzdCwgaWQ9YXMubGlzdChyb3duYW1lcyhkYXRhKSksIG1ldGFkYXRhPU5BLCBTSU1QTElGWT1GQUxTRSkKICB9CiAgIyBUaGUgc2FtcGxlcyAvIHNpdGVzIC8gY29sdW1ucyAibWV0YSIgZGF0YSB0YWJsZQogIGlmKCFpcy5udWxsKHNhbXBsZV9tZXRhZGF0YSkpewogICAgY29sdW1ucyA9IG1hcHBseShsaXN0LCBTSU1QTElGWT1GQUxTRSwgaWQ9YXMubGlzdChjb2xuYW1lcyhkYXRhKSksCiAgICAgICAgICAgICAgICAgICAgIG1ldGFkYXRhPWFscGx5KGFzLm1hdHJpeChzYW1wbGVfbWV0YWRhdGEpLCAxLCAuZXhwYW5kPUZBTFNFLCAuZGltcz1UUlVFKSkgCiAgfSBlbHNlIHsKICAgIGNvbHVtbnMgPSBtYXBwbHkobGlzdCwgaWQ9YXMubGlzdChjb2xuYW1lcyhkYXRhKSksIG1ldGFkYXRhPU5BLCBTSU1QTElGWT1GQUxTRSkKICB9CiAgIyBDb252ZXJ0IHRoZSBjb250aW5nZW5jeSB0YWJsZSB0byBhIGxpc3QKICBkYXRhbGlzdCA9IGFzLmxpc3QoYXMuZGF0YS5mcmFtZShhcyh0KGRhdGEpLCAibWF0cml4IikpKQogIG5hbWVzKGRhdGFsaXN0KSA8LSBOVUxMCiAgIyBEZWZpbmUgdGhlIGxpc3QsIGluc3RhbnRpYXRlIGFzIGJpb20tZm9ybWF0LCBhbmQgcmV0dXJuCiAgIyAoTWlnaHQgZXZlbnR1YWxseSBleHBvc2Ugc29tZSBvZiB0aGVzZSBsaXN0IGVsZW1lbnRzIGFzIGZ1bmN0aW9uIGFyZ3VtZW50cykKICBmb3JtYXRfdXJsID0gImh0dHA6Ly9iaW9tLWZvcm1hdC5vcmciCiAgcmV0dXJuKGJpb20obGlzdChpZD1pZCwKICAgICAgICAgICAgICAgICAgIGZvcm1hdCA9ICJCaW9sb2dpY2FsIE9ic2VydmF0aW9uIE1hdHJpeCAxLjAuMCIsCiAgICAgICAgICAgICAgICAgICBmb3JtYXRfdXJsID0gZm9ybWF0X3VybCwKICAgICAgICAgICAgICAgICAgIHR5cGUgPSAiT1RVIHRhYmxlIiwKICAgICAgICAgICAgICAgICAgIGdlbmVyYXRlZF9ieSA9IHNwcmludGYoImJpb21mb3JtYXQgJXMiLCBwYWNrYWdlVmVyc2lvbigiYmlvbWZvcm1hdCIpKSwKICAgICAgICAgICAgICAgICAgIGRhdGUgPSBhcy5jaGFyYWN0ZXIoU3lzLnRpbWUoKSksCiAgICAgICAgICAgICAgICAgICBtYXRyaXhfdHlwZSA9ICJkZW5zZSIsCiAgICAgICAgICAgICAgICAgICBtYXRyaXhfZWxlbWVudF90eXBlID0gbWF0cml4X2VsZW1lbnRfdHlwZSwKICAgICAgICAgICAgICAgICAgIHNoYXBlID0gZGltKGRhdGEpLAogICAgICAgICAgICAgICAgICAgcm93cyA9IHJvd3MsCiAgICAgICAgICAgICAgICAgICBjb2x1bW5zID0gY29sdW1ucywKICAgICAgICAgICAgICAgICAgIGRhdGEgPSBkYXRhbGlzdCkKICApKQp9CmBgYAoKV3JpdGUgdGhlIGJpb20gZmlsZSBhbmQgcnVuIGJvdGggdGhlIGZhc3RhIGFuZCB0aGUgYmlvbSBmaWxlcyBpbiB0aGUgW0dhbGF4eSBzZXJ2ZXJdKGh0dHBzOi8vdXNlZ2FsYXh5Lm9yZykuIFRoZSBtdWx0aXBsZSBhbGlnbm1lbnQgb2YgdGhlIHNlcXVlbmNlcyB3YXMgcHJvdmlkZWQgd2l0aCB0aGUgTUFGRlQgcHJvZ3JhbSAoW0thdG9oLCAyMDAyXShodHRwczovL2RvaToxMC4xMDkzL25hci9na2Y0MzYpKSBhbmQgd2UgaW5mZXJyZWQgdGhlIHBoeWxvZ2VuZXRpYyB0cmVlIHdpdGggdGhlIEZhc3RUcmVlIDIgdG9vbCAoW1ByaWNlIGV0IGFsLiwgMjAxMF0oaHR0cHM6Ly9kb2k6MTAuMTM3MS9qb3VybmFsLnBvbmUuMDAwOTQ5MCkpIGFuZCBQaGFuZ29ybiBwYWNrYWdlIHYyLjguMSAoW1NjaGxpZXAsIDIwMTFdKGh0dHBzOi8vZG9pOjEwLjEwOTMvYmlvaW5mb3JtYXRpY3MvYnRxNzA2KSkuCgpgYGB7ciBiaW9tIGNyZWF0aW9uLCBldmFsID0gRn0Kb3R1IDwtdChhcyhvdHVfdGFibGUocHNfcmZmKSwibWF0cml4IikpICMgJ3QnIHRvIHRyYW5zZm9ybSBpZiB0YXhhX2FyZV9yb3dzPUZBTFNFCmJpb20gPC0gbWFrZV9iaW9tKGRhdGE9IG90dSkKd3JpdGVfYmlvbShiaW9tLCJiaW9tIikgI2NyZWF0ZSBiaW9tIGZpbGUKI1J1biBib3RoIGZpbGVzIGluIEdhbGF4eSBGcm9ncyB0byBjb25zdHJ1Y3QgdGhlIHBoeWxvZ2VuZXRpYyB0cmVlCmBgYAoKVGhlIG91dHB1dCBkZWxpdmVyZWQgYnkgdGhlIHNlcnZlciBpbiBhIG5ld2ljayBmaWxlIHRoYXQgbWlnaCBiZSBpbmNsdWRlIG5vdyBpbiB0aGUgcGh5bG9zZXEgb2JqZWN0Lgo+IE5vdGUgdGhhdCB0aGlzIHN0ZXAgY2FuIGJlIG1hZGUgd2l0aCB0aGUgcHJva2FyeW90aWMgcGh5bG9zZXEgb2JqZWN0IGJ1dCB0aGUgcHJvY2VzcyB3aWxsIGJlIGxvbmdlci4gSXQgY2FuIGJlIGFuIGhlYXZ5IHRhc2sgdG8gcnVuIGRlcGVuZGluZyBvbiB0aGUgd2VpZ2h0IG9mIHlvdXIgZmFzdGEgZmlsZS4gSGVyZSBpcyBhIHZlcnkgc21hbGwgZGF0YXNldCBvZiBzZXF1ZW5jZXMuIAoKYGBge3IgbWVyZ2UgdHJlZSwgZXZhbCA9Rn0KdHJlZSA8LSBhcGU6OnJlYWQudHJlZShwYXN0ZTAoZGlyX2RhdGFzZXQsICJ0cmVlLm53ayIpKQpwc19yZmYgPC0gbWVyZ2VfcGh5bG9zZXEocHNfcmZmLCB0cmVlKQpzYXZlKHBzX3JmZiwgZmlsZSA9IHBhc3RlMChkaXJfZGF0YXNldCwicHNfcmZmLlJEYXRhIikpCmBgYAoKIyMjIE9UVXMgY2x1c3RlcmluZwpUaGlzIHN0ZXAgYWxsb3dzIHRvIHF1aWNrbHkgb2J0YWluIE9UVSB3aXRoIDg4JSwgOTMlIGFuZCA5NyUgY2x1c3RlcmluZyB0aHJlc2hvbGRzIGFuZCBjcmVhdGUgbmV3IHBoeWxvc2VxIG9iamVjdHMgd2l0aCB0aGVzZSBmb3JtaW5nIE9UVXMuIFNlZSAKCmBgYHtyIE9UVXMgY3JlYXRpb24sIGV2YWwgPUZ9Cm5wcm9jIDwtIDQgIyBzZXQgdG8gbnVtYmVyIG9mIGNwdXMvcHJvY2Vzc29ycyB0byB1c2UgZm9yIHRoZSBjbHVzdGVyaW5nCmRuYSA8LSByZWZzZXEocHNfcmZmKQojIyBGaW5kIGNsdXN0ZXJzIG9mIEFTVnMgdG8gZm9ybSB0aGUgbmV3IE9UVXMKYWxuIDwtIERFQ0lQSEVSOjpBbGlnblNlcXMoZG5hLCBwcm9jZXNzb3JzID0gbnByb2MpCmQgPC0gREVDSVBIRVI6OkRpc3RhbmNlTWF0cml4KGFsbiwgcHJvY2Vzc29ycyA9IG5wcm9jKQoKY2x1c3RlcnMgPC0gREVDSVBIRVI6OlRyZWVMaW5lKAogIG15RGlzdE1hdHJpeD1kLAogIG1ldGhvZCA9ICJjb21wbGV0ZSIsCiAgY3V0b2ZmID0gYygwLjEyLCAwLjA3LDAuMDMpLCAjIDg4JSwgOTMlIGFuZCA5NyUgT1RVIGNsdXN0ZXJpbmcKICB0eXBlID0gImNsdXN0ZXJzIiwKICBwcm9jZXNzb3JzID0gbnByb2MpCgojIyBVc2UgZHBseXIgdG8gbWVyZ2UgdGhlIGNvbHVtbnMgb2YgdGhlIHNlcXRhYiBtYXRyaXggZm9yIEFTVnMgaW4gdGhlIHNhbWUgT1RVCiMgcHJlcCBieSBhZGRpbmcgc2VxdWVuY2VzIHRvIHRoZSBgY2x1c3RlcnNgIGRhdGEgZnJhbWUKY2x1c3RlcnMgPC0gY2x1c3RlcnMgJT4lICBhZGRfY29sdW1uKHNlcXVlbmNlID0gYXN2X3NlcXVlbmNlcykKcHNfOTcgPC0gbWVyZ2VfdGF4YV92ZWMoCiAgcHNfcmZmLCAKICBncm91cCA9IGNsdXN0ZXJzJGNsdXN0ZXIwXzAzY29tcGxldGUsCiAgdGF4X2FkanVzdCA9IDIKKQoKcHNfOTMgPC0gbWVyZ2VfdGF4YV92ZWMoCiAgcHNfcmZmLCAKICBncm91cCA9IGNsdXN0ZXJzJGNsdXN0ZXIwXzA3Y29tcGxldGUsCiAgdGF4X2FkanVzdCA9IDIKKQoKcHNfODggPC0gbWVyZ2VfdGF4YV92ZWMoCiAgcHNfcmZmLCAKICBncm91cCA9IGNsdXN0ZXJzJGNsdXN0ZXIwXzEyY29tcGxldGUsCiAgdGF4X2FkanVzdCA9IDIKKQoKc2F2ZShwc185Nyxwc185MywgcHNfODgsIGZpbGUgPSBwYXN0ZTAoZGlyX2RhdGFzZXQsICJwc19PVFVzLlJEYXRhIikpCmBgYAoKIyMjIE1pY3JvYmlhbCBjb21wb3NpdGlvbgoKQ3JlYXRlIGEgZGF0YSB0YWJsZSAqKnBoeXNlcSoqIGZyb20gdGhlIHBoeWxvc2VxIG9iamVjdCB0aGF0IHdpbGwgYmUgdXNlZCB0byBjb25zdHJ1Y3QgYmFycGxvdAoKYGBge3IgcGh5c2VxLCBldmFsPSBULCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHMgPSAnaGlkZSd9CmxvYWQoZmlsZSA9IHBhc3RlMChkaXJfZGF0YXNldCwgInBzX3JmZi5SRGF0YSIpKQpwaHlzZXEgPC0gcHNfcmZmICU+JQogIHRheF9nbG9tKHRheHJhbmsgPSAiQVNWIikgJT4lICAgICAgICAgICAgICAgICAgICAgIyBhZ2dsb21lcmF0ZSBhdCBwaHlsdW0gbGV2ZWwKICB0cmFuc2Zvcm1fc2FtcGxlX2NvdW50cyhmdW5jdGlvbih4KSB7eC9zdW0oeCl9ICkgJT4lICMgVHJhbnNmb3JtIHRvIHJlbC4gYWJ1bmRhbmNlCiAgcHNtZWx0KCkgCgpwaHlzZXEkSUQgPC0gZmFjdG9yKHBoeXNlcSRJRCwgbGV2ZWxzID0gdW5pcXVlKGFycmFuZ2UocGh5c2VxLCBwaHlzZXEkQVM3MikkSUQpKQpwaHlzZXEkQWJ1bmRhbmNlIDwtIHBoeXNlcSRBYnVuZGFuY2Uvc3VtKHBoeXNlcSRBYnVuZGFuY2UpICogMTAwICMgVG8gb2J0YWluICUKd3JpdGUudGFibGUocGh5c2VxLCBmaWxlID0gcGFzdGUwKGRpcl9kYXRhc2V0LCAicGh5c2VxLnR4dCIpLCBzZXAgPSJcdCIsIHJvdy5uYW1lcyA9IEYpCmBgYAoKQ3JlYXRlIHR3byBiYXJwbG90cyBmb3IgYm90aCBtaWNyb2JpYWwgUGh5bGEgYW5kIEZhbWlsaWVzLgoKIyMjIyBQaHlsdW0gY29tcG9zaXRpb24KCmBgYHtyIFBoeWx1bSBjb21wbywgZXZhbCA9IFQsIHJlc3VsdHM9J2hpZGUnLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcuaGVpZ2h0PSA3LCBmaWcud2lkdGg9MTR9CnBoeXNlcSA8LSByZWFkLnRhYmxlKGZpbGUgPSBwYXN0ZTAoZGlyX2RhdGFzZXQsICJwaHlzZXEudHh0IiksIHNlcCA9Ilx0IiwgaGVhZGVyID0gVCkKCmNvbXBvX3BoeWx1bSA8LSBwaHlzZXEgJT4lCiAgZ3JvdXBfYnkoUGh5bHVtKSAlPiUKICBzdW1tYXJpc2UoUHJvcF9waHlsdW0gPSBzdW0oQWJ1bmRhbmNlKSkgJT4lCiAgYXJyYW5nZShkZXNjKFByb3BfcGh5bHVtKSkKCnBoeXNlcSRQaHlsdW0gPC0gZmFjdG9yKHBoeXNlcSRQaHlsdW0sIGxldmVscyA9IHJldihhcy5jaGFyYWN0ZXIoY29tcG9fcGh5bHVtJFBoeWx1bSkpKQoKRmlnX3BoeWx1bSA8LSBwaHlzZXEgJT4lCiAgZ3JvdXBfYnkoSUQsIFBoeWx1bSkgJT4lCiAgc3VtbWFyaXNlKFByb3BfcGh5bHVtID0gc3VtKEFidW5kYW5jZSkpICU+JQogIGdncGxvdChhZXMoeCA9IElELCB5ID0gUHJvcF9waHlsdW0sIGZpbGwgPSBQaHlsdW0pKSArIAogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249ImZpbGwiLCBjb2xvciA9ICJibGFjayIsbGluZXdpZHRoID0gMC41KSArIAogIHlsYWIoIlJlbGF0aXZlIEFidW5kYW5jZSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLDApKSArICNyZW1vdmUgdGhlIHNwYWNlIGJlbG93IHRoZSAwIG9mIHRoZSB5IGF4aXMgaW4gdGhlIGdyYXBoCiAgdGhlbWVfYncoKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPSByZXYoYygiZG9kZ2VyYmx1ZTQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImRhcmtvbGl2ZWdyZWVuIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJkYXJrcmVkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcmFuZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm1hZ2VudGEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInllbGxvdyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZ3JheSIpKSkrCiAgdGhlbWUoYXhpcy5saW5lID0gZWxlbWVudF9saW5lKHNpemUgPSAwKSwgCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgICNyZW1vdmUgbWFqb3ItZ3JpZCBsYWJlbHMKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJzZXJpZiIsc2l6ZSA9IDE4KSwgCiAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksIAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFtaWx5ID0gInNlcmlmIiwgYW5nbGUgPSA0NSxoanVzdCA9IDEpLCAKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiLCBzaXplID0gMTQpLAogICAgICAgIGF4aXMudGl0bGUueCA9ICBlbGVtZW50X3RleHQoc2l6ZSA9IDApLAogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSwgIGZhbWlseSA9ICJzZXJpZiIpLCAKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4LCBmYW1pbHkgPSAic2VyaWYiKSkrCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKG5jb2wgPTEpKSsKICBsYWJzKGZpbGwgPSAiTWljcm9iaWFsIFBoeWx1bSIpCkZpZ19waHlsdW0KCnBkZihmaWxlID0gcGFzdGUwKGRpcl9ncmFwaCwiY29tcG9fUGh5bHVtLnBkZiIpLCBoZSA9IDcgLCB3aSA9IDE0KQpGaWdfcGh5bHVtCmRldi5vZmYoKQoKYGBgCgoKIyMjIyBGYW1pbHkgY29tcG9zaXRpb24KYGBge3IgZmFtaWx5IGNvbXBvLCBldmFsID0gVCwgcmVzdWx0cz0naGlkZScsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGZpZy5oZWlnaHQ9IDcsIGZpZy53aWR0aD0xNH0KcGh5c2VxIDwtIHJlYWQudGFibGUoZmlsZSA9IHBhc3RlMChkaXJfZGF0YXNldCwgInBoeXNlcS50eHQiKSwgc2VwID0iXHQiLCBoZWFkZXIgPSBUKQpjb21wb19mYW1pbHkgPC0gcGh5c2VxICU+JQogIGdyb3VwX2J5KFBoeWx1bSwgRmFtaWx5KSAlPiUKICBzdW1tYXJpc2UoUHJvcF9mYW1pbHkgPSBzdW0oQWJ1bmRhbmNlKSkgJT4lCiAgYXJyYW5nZShkZXNjKFByb3BfZmFtaWx5KSkgIyBhcnJhbmdlIGJ5IHByb3AgaW4gb3JkZXIgdG8gc2VsZWN0IHRoZW4gdGhlIG1vc3QgYWJ1bmRhbnQKCnBoeXNlcSRGYW1pbHkgPC0gZmFjdG9yKHBoeXNlcSRGYW1pbHksIGxldmVscyA9IHJldihhcy5jaGFyYWN0ZXIoY29tcG9fZmFtaWx5JEZhbWlseSkpKQpyZXYobGV2ZWxzKHBoeXNlcSRGYW1pbHkpKVsxOjIwXSAjIEhlcmUsIHdlIHNlbGVjdCB0aGUgMjAgbW9zdCBhYnVuZGFudCBmYW1pbGllcwpwaHlzZXEkRmFtaWx5IDwtIGZjdF9vdGhlcihwaHlzZXEkRmFtaWx5LCBrZWVwID0gcmV2KGxldmVscyhwaHlzZXEkRmFtaWx5KSlbMToyMF0pCgojIFNlbGVjdCBmYW1pbHkgY29sb3JzIGRlcGVuZGluZyBvbiB0aGUgYmVsb25naW5nIHBoeWx1bQpjb3VudHMgPC0gY29tcG9fZmFtaWx5WzE6MjAsXSAlPiUgY291bnQoUGh5bHVtKQpmYW1fY29sIDwtIGNiaW5kKGNvbXBvX2ZhbWlseVsxOjIwLF0gJT4lIAogICAgICAgICAgICAgICAgICAgYXJyYW5nZShQaHlsdW0sIFByb3BfZmFtaWx5KSwgCiAgICAgICAgICAgICAgICAgIkNvbG9ycyIgPSBjKCJiaXNxdWUzIiwib3JhbmdlIiwgI2ZvciBhY3Rpbm9iYWN0ZXJpYQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZGFya3JlZCIsImRhcmttYWdlbnRhIiAsImZpcmVicmljayIsICMgZm9yIGJhY3Rlcm9pZGV0ZXMKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImdyZWVuIiwiZGFya2dyZWVuIiwgImRhcmtvbGl2ZWdyZWVuMSIsImRhcmtvbGl2ZWdyZWVuIiAsICMgZm9yIGZpcm1pY3V0ZXMKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImRhcmtzbGF0ZWdyYXk0IiwiZGFya3NsYXRlZ3JheTMiLCJjYWRldGJsdWUxIiwiZGFya3R1cnF1b2lzZSIsInR1cnF1b2lzZSIsICNmb3IgcHJvdGVvLCBtZWx0IHRoZSBncmFkaWVudAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiY3lhbiIsImNvcm5mbG93ZXJibHVlIiwiZG9kZ2VyYmx1ZSIsICJibHVlIiwiZGFya2JsdWUiLCJkb2RnZXJibHVlNCIpKSAKCmJhcnBsb3QgPC0gcGh5c2VxICU+JQogIGdyb3VwX2J5KElELCBGYW1pbHkpICU+JQogIHN1bW1hcmlzZShQcm9wX2ZhbWlseSA9IHN1bShBYnVuZGFuY2UpKSAKYmFycGxvdCA8LSBsZWZ0X2pvaW4oYmFycGxvdCwgZmFtX2NvbCAlPiUgYXMuZGF0YS5mcmFtZSgpICU+JSBzZWxlY3QoRmFtaWx5LENvbG9ycyksIGJ5ID0gIkZhbWlseSIpCmJhcnBsb3QkQ29sb3JzIDwtIGZjdF9leHBsaWNpdF9uYShiYXJwbG90JENvbG9ycywgbmFfbGV2ZWwgPSAiZGltZ3JheSIpCmJhcnBsb3QkRmFtaWx5IDwtIGZhY3RvcihiYXJwbG90JEZhbWlseSwgbGV2ZWxzID0gYyhmYW1fY29sJEZhbWlseSwgIk90aGVyIikpCmJhcnBsb3QkQ29sb3JzIDwtIGZhY3RvcihiYXJwbG90JENvbG9ycywgbGV2ZWxzID0gYyhmYW1fY29sJENvbG9ycywgImRpbWdyYXkiKSkKYmFycGxvdCRJRCA8LSBmYWN0b3IoYmFycGxvdCRJRCwgbGV2ZWxzID0gdW5pcXVlKGFycmFuZ2UocGh5c2VxLCBwaHlzZXEkVG90YWwpJElEKSkKCkZpZ19mYW0gPC0gYmFycGxvdCAlPiUKICBnZ3Bsb3QoYWVzKHggPSBJRCwgeSA9IFByb3BfZmFtaWx5LCBmaWxsID0gRmFtaWx5KSkgKyAKICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uPSJmaWxsIixjb2xvciA9ICJibGFjayIsIGxpbmV3aWR0aCA9IDAuNSkgKyAKICB5bGFiKCJSZWxhdGl2ZSBBYnVuZGFuY2UiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwwKSkgKyAjcmVtb3ZlIHRoZSBzcGFjZSBiZWxvdyB0aGUgMCBvZiB0aGUgeSBheGlzIGluIHRoZSBncmFwaAogIHRoZW1lX2J3KCkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz0gbGV2ZWxzKGJhcnBsb3QkQ29sb3JzKSkrCiAgdGhlbWUoYXhpcy5saW5lID0gZWxlbWVudF9saW5lKHNpemUgPSAwKSwgCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgICNyZW1vdmUgbWFqb3ItZ3JpZCBsYWJlbHMKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJzZXJpZiIsc2l6ZSA9IDE4KSwgCiAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksIAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFtaWx5ID0gInNlcmlmIiwgYW5nbGUgPSA0NSxoanVzdCA9IDEpLCAKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiLCBzaXplID0gMTQpLAogICAgICAgIGF4aXMudGl0bGUueCA9ICBlbGVtZW50X3RleHQoc2l6ZSA9IDApLAogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSwgIGZhbWlseSA9ICJzZXJpZiIpLCAKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4LCBmYW1pbHkgPSAic2VyaWYiKSkrCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKG5jb2wgPTEpKSsKICBsYWJzKGZpbGwgPSAiTWljcm9iaWFsIEZhbWlseSIpCkZpZ19mYW0KCnBkZihmaWxlID0gcGFzdGUwKGRpcl9ncmFwaCwgIkZpZ19mYW1pbHkucGRmIiksIGhlID0gNyAsIHdpID0gMTQpCkZpZ19mYW0KZGV2Lm9mZigpCmBgYAoKIyMjIExFZlNlIGFuYWx5c2lzCgpXZSB3YW50IHRvIGRldGVybWluZSBpZiBpdCBkb2VzIGV4aXN0IGRpc2NyaW1pbmFudCBtaWNyb2JpYWwgdGF4YSB0aGF0IGNhbiBiZSBjb25zaWRlcmVkIGFzIGNhbmRpZGF0ZSB0byBiZSBtYW5pcHVsYXRpdmUgbWljcm9vcmdhbmlzbXMuIApJbiB0aGF0IHdheSwgd2UgcnVuIGEgTGluZWFyIEVmZmVjdCBTaXplIGRpc2NyaW1pbmFudCBhbmFseXNpcyBpbiBvcmRlciB0byBkaXNjcmltaW5hdGUgdGF4YSB0aGF0IGFyZSBzdGF0aXN0aWNhbGx5IG1vcmUgcHJldmFsZW50IGFuZCBhYnVuZGFudCBpbiBhbiBhZ2dyZWdhdGlvbiBzdGF0dXMKCmBgYHtyIGxlZnNlLCBldmFsID0gRn0KbG9hZChwYXN0ZTAoZGlyX2RhdGFzZXQsICJwc19yZmYuUkRhdGEiKSkKc2FtcF9kYXRhIDwtIHBzX3JmZiAlPiUgc2FtcGxlX2RhdGEoKSAlPiUgYXMuZGF0YS5mcmFtZSgpCgpvdHVfYXN2IDwtIGNiaW5kKHJvd25hbWVzKHBzX3JmZiAlPiUgb3R1X3RhYmxlKCkgJT4lIGFzLmRhdGEuZnJhbWUoKSAlPiUgdCgpKSwKICAgICAgICAgICAgICAgICBwc19yZmYgJT4lIG90dV90YWJsZSgpICU+JSBhcy5kYXRhLmZyYW1lKCkgJT4lIHQoKSkKY29sbmFtZXMob3R1X2FzdikgPC0gYygiU3RhdHVzIiwgc2FtcF9kYXRhJHN0YXR1cykKd3JpdGUudGFibGUob3R1X2FzdiwgZmlsZSA9IHBhc3RlMChkaXJfZGF0YXNldCwgImxlZnNlX3RhYl9hc3YudHh0IiksIHNlcCA9ICJcdCIsIHJvdy5uYW1lcyA9IEYpCmBgYAoKPiBSdW4gdGhlIExFZlNlIGluIHRoZSBbR2FsYXh5IHNlcnZlcl0oaHR0cHM6Ly91c2VnYWxheHkub3JnKS4gRG93bmxvYWQgdGhlIG91dHB1dCBvZiB0aGUgYW5hbHlzaXMgaW4gdGhlICJkaXJfZGF0YXNldCIgYW5kIGNhbGwgaXQgIkxEQV9FZmZlY3RfU2l6ZV9BU1YiCgpgYGB7ciByZXN1bHQgbGVmc2UsIGV2YWwgPSBGfQpMREFfRWZmZWN0X1NpemVfQVNWIDwtIHJlYWQuZGVsaW0ocGFzdGUwKGRpcl9kYXRhc2V0LCAiTERBX0VmZmVjdF9TaXplX0FTViIpLCBoZWFkZXI9RkFMU0UpCgpMREFfcmVzdWx0IDwtIGNiaW5kKExEQV9FZmZlY3RfU2l6ZV9BU1YsICJDbHVzdGVyaW5nIiA9IHJlcCgiMTAwJSIpKSAlPiUKICBmaWx0ZXIoVjMgJWluJSBjKCJIaWdoIiwgIkxvdyIpKQpjb2xuYW1lcyhMREFfcmVzdWx0KSA8LSBjKCJBU1YiLCJMREFfcmVzIiAsICJEaXNjcmltaW5hbnQiICwgIkxEQV9yZXMyIiAsICJwLXZhbHVlIiwgIkNsdXN0ZXJpbmciKQpMREFfcmVzdWx0IDwtIGxlZnRfam9pbihMREFfcmVzdWx0LCBwc19yZmYgJT4lIHN1YnNldF90YXhhKEFTViAlaW4lIExEQV9yZXN1bHQkQVNWKSAlPiUgdGF4X3RhYmxlKCkgJT4lIGFzLmRhdGEuZnJhbWUoKSwgYnkgPSAiQVNWIikKTERBX3Jlc3VsdCRUYXhvbm9teSA8LSBwYXN0ZShMREFfcmVzdWx0JEFTViwgTERBX3Jlc3VsdCRHZW51cywgTERBX3Jlc3VsdCRDbGFzcywgc2VwID0gInwiKQp3cml0ZS50YWJsZShMREFfcmVzdWx0LCBmaWxlPXBhc3RlMChkaXJfZGF0YXNldCwgIkxEQV9yZXN1bHQudHh0IiksIHNlcD0iXHQiLCByb3cubmFtZXM9RiwgY29sLm5hbWVzPVQpCgoKdGFiX2FzdiA8LSBsZWZ0X2pvaW4oCiAgbGVmdF9qb2luKCAgCiAgICBjYmluZCgiU3RhdHVzIiA9IGNvbG5hbWVzKG90dV9hc3YpLCAKICAgICAgICAgIG90dV9hc3YgJT4lIAogICAgICAgICAgICB0KCkgJT4lCiAgICAgICAgICAgIGRhdGEuZnJhbWUoKSAlPiUKICAgICAgICAgICAgc2VsZWN0KExEQV9yZXN1bHQkQVNWKSAlPiUKICAgICAgICAgICAgbXV0YXRlX2lmKGlzLmNoYXJhY3RlciwgYXMubnVtZXJpYykpICU+JQogICAgICBwaXZvdF9sb25nZXIoIVN0YXR1cywgbmFtZXNfdG8gPSAiQVNWIiwgdmFsdWVzX3RvID0gIkFidW5kYW5jZSIpICU+JQogICAgICBncm91cF9ieShBU1YsIFN0YXR1cykgJT4lCiAgICAgIHN1bW1hcmlzZShBYnVuZGFuY2UgPSBzdW0oQWJ1bmRhbmNlKSksIAogICAgY2JpbmQoIlN0YXR1cyIgPSBjb2xuYW1lcyhvdHVfYXN2KSwgCiAgICAgICAgICBvdHVfYXN2ICU+JSAKICAgICAgICAgICAgdCgpICU+JQogICAgICAgICAgICBkYXRhLmZyYW1lKCkgJT4lCiAgICAgICAgICAgIHNlbGVjdChMREFfcmVzdWx0JEFTVikgJT4lCiAgICAgICAgICAgIG11dGF0ZV9pZihpcy5jaGFyYWN0ZXIsIGFzLm51bWVyaWMpKSAlPiUKICAgICAgcGl2b3RfbG9uZ2VyKCFTdGF0dXMsIG5hbWVzX3RvID0gIkFTViIsIHZhbHVlc190byA9ICJBYnVuZGFuY2UiKSAlPiUKICAgICAgZmlsdGVyKEFidW5kYW5jZSAhPSAwKSAlPiUKICAgICAgY291bnQoQVNWLCBTdGF0dXMsIG5hbWUgPSAiT2NjdXJyZW5jZSIpLAogICAgYnkgPSBjKCJBU1YiLCAiU3RhdHVzIikpLAogIExEQV9yZXN1bHQsIAogIGJ5ID0gIkFTViIpCgp3cml0ZS50YWJsZSh0YWJfYXN2LCBmaWxlID0gcGFzdGUwKGRpcl9kYXRhc2V0LCAidGFiX2Fzdi50eHQiKSwgc2VwID0iXHQiLCByb3cubmFtZXMgPSBGKQpgYGAKCgpgYGB7ciByIHJlc3VsdCBsZWZzZSwgZXZhbCA9IFQsIHJlc3VsdHM9J2hpZGUnLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBmaWcud2lkdGg9IDQsIGZpZy5oZWlnaHQ9MTB9CnRhYl9hc3YgPC0gcmVhZC50YWJsZShwYXN0ZTAoZGlyX2RhdGFzZXQsICJ0YWJfYXN2LnR4dCIpLCBzZXAgPSJcdCIsIGhlYWRlciA9IFQpCgp0YWJfYXN2JFN0YXR1cyA8LSBzdHJfcmVwbGFjZV9hbGwodGFiX2FzdiRTdGF0dXMsIGMoIkxvdyIgPSAiTG93LWFnZ3JlZ2F0aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJIaWdoIiA9ICJIaWdoLWFnZ3JlZ2F0aW9uIikpCnRhYl9hc3YkT2NjdXJyZW5jZSA8LSByZXBsYWNlX25hKHRhYl9hc3YkT2NjdXJyZW5jZSwgMCkKdGFiX2Fzdlt0YWJfYXN2JFN0YXR1cyA9PSAiTG93LWFnZ3JlZ2F0aW9uIixdJE9jY3VycmVuY2UgPC0gdGFiX2Fzdlt0YWJfYXN2JFN0YXR1cyA9PSAiTG93LWFnZ3JlZ2F0aW9uIixdJE9jY3VycmVuY2UqLTEKdGFiX2FzdiA8LSB0YWJfYXN2ICU+JSBhcnJhbmdlKGRlc2MoRGlzY3JpbWluYW50KSkKdGFiX2FzdiRUYXhvbm9teSA8LSBmYWN0b3IodGFiX2FzdiRUYXhvbm9teSwgbGV2ZWxzID0gdGFiX2FzdiRUYXhvbm9teSAlPiUgdW5pcXVlKCkpCnRhYl9hc3YkRGlzY3JpbWluYW50IDwtIGZhY3Rvcih0YWJfYXN2JERpc2NyaW1pbmFudCwgbGV2ZWxzID0gYygiTG93IiwgIkhpZ2giKSkKdGFiX2FzdiRTdGF0dXMgPC0gZmFjdG9yKHRhYl9hc3YkU3RhdHVzLCBsZXZlbHMgPSBjKCJMb3ctYWdncmVnYXRpb24iLCAiSGlnaC1hZ2dyZWdhdGlvbiIpKQoKZ2dwbG90KHRhYl9hc3YsYWVzKHkgPSBUYXhvbm9teSwgeCA9IE9jY3VycmVuY2UsIGZpbGwgPSBTdGF0dXMgLGFscGhhID0gbG9nKEFidW5kYW5jZSkpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gImlkZW50aXR5Iixjb2xvciA9ICJibGFjayIsbGluZXdpZHRoID0gMC4zKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoLTIwLDE5KSkrCiAgeGxhYigiTnVtYmVyIG9mIGZlbWFsZXMgaG9zdGluZyB0aGUgZGlzY3JpbWluYW50IikrCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiZGFya2JsdWUiLCAiZGFya3JlZCIpKSsKICB0aGVtZV9idygpICsKICB0aGVtZShheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoc2l6ZSA9IDApLCAKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCAgI3JlbW92ZSBtYWpvci1ncmlkIGxhYmVscwogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gInNlcmlmIixzaXplID0gMTYpLCAKICAgICAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2KSwgCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCBmYW1pbHkgPSAic2VyaWYiKSwgCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gInNlcmlmIiwgc2l6ZSA9IDE2KSwKICAgICAgICBheGlzLnRpdGxlLnggPSAgZWxlbWVudF90ZXh0KHNpemUgPSAxNiksCiAgICAgICAgYXhpcy50aXRsZS55ID0gIGVsZW1lbnRfdGV4dChzaXplID0gMCksCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCAgZmFtaWx5ID0gInNlcmlmIiksIAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgsIGZhbWlseSA9ICJzZXJpZiIpKSsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQobmNvbCA9MSkpKwogIGxhYnMoZmlsbCA9ICJBZ2dyZWdhdGlvbiBsZXZlbCIsIAogICAgICAgYWxwaGEgPSAiQWJ1bmRhbmNlIChsb2cpIiwgCiAgICAgICB0aXRsZSA9ICJBU1YgRGlzY3JpbWluYW50cyIpCgpgYGAKCgpJdCBleGlzdHMgMTAgQVNWcyBkaXNjcmltaW5hbnRzIGJ1dCB0aGV5IGFyZSBhY3R1YWxseSBpbiB2ZXJ5IGxvdyBvY2N1cnJlbmNlIGluIHRoZSBzYW1wbGVzIHdpdGggc3Ryb25nIHZhcmlhdGlvbiBpbiB0aGVpciBhYnVuZGFuY2UuIEluZGVlZCwgaXQgZG9lcyBub3QgZXhpc3QgYW55IGV4Y2x1c2l2ZSB0aGF0IGFyZSBwcmVzZW50IGluIGFsbCBsZXNzLSBvciBoaWdoLWFnZ3JlZ2F0aW9uIGZlbWFsZXMuCgoKIyMjIERpdmVyc2l0eSBjYWxjdWxzCgpXZSB0aGVuIGNhbGN1bGF0ZWQgdGhlIGFscGhhIGRpdmVyc2l0eSBhbmQgYmV0YS1kaXZlcnNpdHkgIG9mIHRoZSBndXQgbWljcm9iaW90YSBvZiB0aGUgMzkgdGVzdGVkIGZlbWFsZXMuCgojIyMjIEFscGhhIGRpdmVyc2l0eSAKClJlZ2FyZGluZyB0aGUgYWxwaGEgZGl2ZXJzaXR5LCB3ZSB1c2VkIHRoZSBkaXJlY3Qgb2JzZXJ2ZWQgcmljaG5lc3MgYXMgYSBwcm94eSBmb3IgdGhlIHF1YWxpdGF0aXZlIHRheG9ub21pYyByaWNobmVzcyBhbmQgdGhlIFNoYW5ub24gZW50cm9weSBmb3IgaXRzIHF1YW50aXRhdGl2ZSBlcXVpdmFsZW50LiBUaGVpciByZXNwZWN0aXZlIGVxdWl2YWxlbnRzIHRoYXQgaW5jbHVkZSB0aGUgcGh5bG9nZW5ldGljIHJlbGF0ZWRuZXNzIGJldHdlZW4gbWljcm9iaWFsIHNlcXVlbmNlcyBhcmUgdGhlIEZhaXRoIGFuZCB0aGUgSGlsbDEgKHEgPSAxKSBpbmRpY2VzIChbQ2hhbyBldCBhbC4sIDIwMTBdKGh0dHBzOi8vZG9pOjEwLjEwOTgvcnN0Yi4yMDEwLjAyNzIpKS4gCgoKYGBge3IgYWxwaGEsIGV2YWwgPSBUfQpsb2FkKGZpbGUgPSBwYXN0ZTAoZGlyX2RhdGFzZXQgLCJwc19yZmYuUkRhdGEiKSkKCmFscGhhX2RhdGEgPC0gbGVmdF9qb2luKAogIGNiaW5kKCJJRCIgPSBzYW1wbGVfbmFtZXMocHNfcmZmKSwKICAgICAgICAiT2JzZXJ2ZWQucmljaG5lc3MiID0gcGQoc2FtcD0gcHNfcmZmQG90dV90YWJsZSAsIHRyZWUgPSBwc19yZmZAcGh5X3RyZWUgLCBpbmNsdWRlLnJvb3QgPSAgVCkkU1IsCiAgICAgICAgZXN0aW1hdGVfcmljaG5lc3MocHNfcmZmICwgbWVhc3VyZXMgPSBjKCJTaGFubm9uIikpLAogICAgICAgICJGYWl0aCIgPSBwZChzYW1wPSBwc19yZmZAb3R1X3RhYmxlICwgdHJlZSA9IHBzX3JmZkBwaHlfdHJlZSAsIGluY2x1ZGUucm9vdCA9ICBUKSRQRCwKICAgICAgICAiSGlsbDEiID0gaGlsbF9waHlsbyhjb21tID0gcHNfcmZmQG90dV90YWJsZSwgdHJlZSA9IHBzX3JmZkBwaHlfdHJlZSwgcSA9IDEpKSAlPiUKICAgIGFzLmRhdGEuZnJhbWUoKSwKICBwc19yZmYgJT4lIHNhbXBsZV9kYXRhKCkgJT4lIGFzLmRhdGEuZnJhbWUoKSwKICBieSA9ICJJRCIpCgp3cml0ZS50YWJsZShhbHBoYV9kYXRhLCBmaWxlID0gcGFzdGUwKGRpcl9kYXRhc2V0LCAiYWxwaGFfZGF0YS50eHQiKSwgc2VwID0gIlx0Iiwgcm93Lm5hbWVzID0gRikKYGBgCgoKIyMjIyBCZXRhIGRpdmVyc2l0eSAKClByZXBhcmF0aW9uIG9mIHRoZSBtYXRyaXggYW5kIHRoZSB0YXhvbm9teSB0YWJsZSB0byBjYWxjdWwgdGhlIGRpc3RhbmNlcyBiZXR3ZWVuIHNhbXBsZSBwYWlycy4KYGBge3IgYmV0YSBwcmVwLCBldmFsID0gVH0KbG9hZChwYXN0ZTAoZGlyX2RhdGFzZXQsICJwc19yZmYuUkRhdGEiKSkKc2FtcF9kYXRhIDwtIHBzX3JmZiAlPiUgc2FtcGxlX2RhdGEoKSAlPiUgYXMuZGF0YS5mcmFtZSgpCnNhbXBfZGF0YSRzdGF0dXMgPC0gc3RyX3JlcGxhY2VfYWxsKHNhbXBfZGF0YSRzdGF0dXMgLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygiTG93IiA9ICJMb3ctYWdncmVnYXRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJIaWdoIiA9ICJIaWdoLWFnZ3JlZ2F0aW9uIikpCnNhbXBfZGF0YSRzdGF0dXMgPC0gZmFjdG9yKHNhbXBfZGF0YSRzdGF0dXMsIGxldmVscyA9IGMoIkxvdy1hZ2dyZWdhdGlvbiIsICJIaWdoLWFnZ3JlZ2F0aW9uIikpCm90dV90YWJsZSA8LSBwc19yZmZAb3R1X3RhYmxlICU+JSBhcy5kYXRhLmZyYW1lKCkKdGF4X3RhYmxlIDwtIHBzX3JmZkB0YXhfdGFibGUgJT4lIGFzLmRhdGEuZnJhbWUoKQp3cml0ZS50YWJsZShvdHVfdGFibGUsIGZpbGUgPSBwYXN0ZTAoZGlyX2RhdGFzZXQsICJvdHVfdGFibGUudHh0IiksIHNlcCA9ICJcdCIsIHJvdy5uYW1lcyA9IFQsIGNvbC5uYW1lcyA9IFQpICAKd3JpdGUudGFibGUodGF4X3RhYmxlLCBmaWxlID0gcGFzdGUwKGRpcl9kYXRhc2V0LCAidGF4X3RhYmxlLnR4dCIpLCBzZXAgPSAiXHQiLCByb3cubmFtZXMgPSBULCBjb2wubmFtZXMgPSBUKQpgYGAKCkZvciBiZXRhLWRpdmVyc2l0eSwgd2UgdXNlZCBhIHRheG9ub21pYyBxdWFsaXRhdGl2ZSBkaXN0YW5jZSB3aXRoIHRoZSBKYWNjYXJkIG1ldHJpYyB0aGF0IGlzIHVzZWQgb24gYSBiaW5hcnkgbWF0cml4IHdpdGggQVNWcyBwcmVzZW5jZS9hYnNlbmNlIGJ5IHNhbXBsZSB3aGlsZSBpdHMgcXVhbnRpdGF0aXZlIGVxdWl2YWxlbnQgQnJheS1DdXJ0aXMgaXMgY2FsY3VsYXRlZCBvbiB0aGUgcmVsYXRpdmUgYWJ1bmRhbmNlcyBvZiBBU1ZzIGluIGVhY2ggc2FtcGxlLiBXZSBhbHNvIHVzZWQgdGhlaXIgcGh5bG9nZW5ldGljIGVxdWl2YWxlbnRzIHdpdGggdGhlIFVuaWZyYWMgbWV0cmljcyBmb3IgcXVhbGl0YXRpdmUgKFVud2VpZ2h0ZWQpIGFuZCBxdWFudGl0YXRpdmUgKFdlaWdodGVkKSBkaXN0YW5jZXMgKFtMb3p1cG9uZSBhbmQgS25pZ2h0LCAyMDA1XShodHRwczovL2RvaToxMC4xMTI4L0FFTS43MS4xMi44MjI4LTgyMzUuMjAwNSksW1lhbmcgZXQgYWwuLCAyMDIxXShodHRwczovL2RvaToxMC4xMDE2L2ouY3Niai4yMDIxLjA3LjAwOSkpLiAKCmBgYHtyIGRpc3RhbmNlIGNhbGN1bCwgZXZhbCA9IFQsIG1lc3NhZ2U9RiwgcmVzdWx0cz1GLCB3YXJuaW5nPUZ9Cm90dS5qYWNjIDwtIHZlZ2Rpc3Qob3R1X3RhYmxlLCBtZXRob2QgPSAiamFjY2FyZCIpCm90dS5iYyA8LSB2ZWdkaXN0KG90dV90YWJsZSwgbWV0aG9kID0gImJyYXkiKQpvdHUudXUgPC0gVW5pRnJhYyhwc19yZmYsIHdlaWdodGVkID0gRiwgbm9ybWFsaXplZD1GLCBwYXJhbGxlbCA9IEYsIGZhc3Q9VCkKb3R1Lnd1IDwtIFVuaUZyYWMocHNfcmZmLCB3ZWlnaHRlZCA9IFQsIG5vcm1hbGl6ZWQ9RiwgcGFyYWxsZWwgPSBGLCBmYXN0PVQpCmBgYAoKVG8gYW5hbHlzZSB0aGUgdmFyaWFiaWxpdHkgaW4gbWljcm9iaWFsIGFzc2VtYmxpZXMgYmV0d2VlbiBsb3ctIGFuZCBoaWdoLWFnZ3JlZ2F0aW9uIGhvc3RzLCB3ZSBhc3Nlc3NlZCB0aGUgZGlzcGVyc2lvbiBmb3IgYWxsIG1ldHJpY3MgKGkuZS4sIGRpc3RhbmNlIG9mIGVhY2ggc2FtcGxlIGZyb20gdGhlIGNlbnRyb8OvZCBhbW9uZyB0aGUgbG93IGFuZCBoaWdoLWFnZ3JlZ2F0aW9uIGxldmVscykgdXNpbmcgdGhlIGJldGFkaXNwZXIgZnVuY3Rpb24gZnJvbSB0aGUgKip2ZWdhbioqIHBhY2thZ2UgKFtPa3NhbmVuIGV0IGFsLiwgMjAyMl0oaHR0cHM6Ly9DUkFOLlItcHJvamVjdC5vcmcvcGFja2FnZT12ZWdhbikpLgoKYGBge3IgZGlzcGVyc2lvbiwgZXZhbCA9IFQsIG1lc3NhZ2U9RiwgcmVzdWx0cz1GLCB3YXJuaW5nPUZ9CmRpc3AuamFjYyA8LSBiZXRhZGlzcGVyKG90dS5qYWNjLCBzYW1wX2RhdGEkc3RhdHVzKQpzaWcuamFjYyA8LSBwZXJtdXRlc3QoZGlzcC5qYWNjKQpEaXN0YW5jZXMuamFjYyA8LSBkaXNwLmphY2MkZGlzdGFuY2VzCmRpc3AuYmMgPC0gYmV0YWRpc3BlcihvdHUuYmMsIHNhbXBfZGF0YSRzdGF0dXMpCnNpZy5iYyA8LSBwZXJtdXRlc3QoZGlzcC5iYykKRGlzdGFuY2VzLmJjIDwtIGRpc3AuYmMkZGlzdGFuY2VzCmRpc3AudXUgPC0gYmV0YWRpc3BlcihvdHUudXUsIHNhbXBfZGF0YSRzdGF0dXMpCkRpc3RhbmNlcy51dSA8LSBkaXNwLnV1JGRpc3RhbmNlcwpzaWcudXUgPC0gcGVybXV0ZXN0KGRpc3AudXUpCmRpc3Aud3UgPC0gYmV0YWRpc3BlcihvdHUud3UsIHNhbXBfZGF0YSRzdGF0dXMpCnNpZy53dSA8LSBwZXJtdXRlc3QoZGlzcC53dSkKRGlzdGFuY2VzLnd1IDwtIGRpc3Aud3UkZGlzdGFuY2VzCgphbHBoYV9kYXRhIDwtIHJlYWQudGFibGUoZmlsZSA9IHBhc3RlMChkaXJfZGF0YXNldCwgImFscGhhX2RhdGEudHh0IiksIHNlcCA9ICJcdCIsIGhlYWRlciA9IFQsIGFsbG93RXNjYXBlcyA9ICkKCm1pY3JvX2Rpdl9kYXRhIDwtIGNiaW5kKGFscGhhX2RhdGEgJT4lIHNlbGVjdChJRDpIaWxsMSksCiAgICAgICAgICAgICAgICAgICAgICAgIkphY2MuZGlzcGVyc2lvbiIgPSBEaXN0YW5jZXMuamFjYywKICAgICAgICAgICAgICAgICAgICAgICAiQkMuZGlzcGVyc2lvbiIgPSBEaXN0YW5jZXMuYmMsCiAgICAgICAgICAgICAgICAgICAgICAgIlVVLmRpc3BlcnNpb24iID0gRGlzdGFuY2VzLnV1LAogICAgICAgICAgICAgICAgICAgICAgICJXVS5kaXNwZXJzaW9uIiA9IERpc3RhbmNlcy53dSwgCiAgICAgICAgICAgICAgICAgICAgICAgYWxwaGFfZGF0YSAlPiUgc2VsZWN0KHNhbXBsaW5nX2RhdGU6d2VpZ2h0KSkKCndyaXRlLnRhYmxlKG1pY3JvX2Rpdl9kYXRhLCBmaWxlID0gcGFzdGUwKGRpcl9kYXRhc2V0LCAibWljcm9fZGl2X2RhdGEudHh0IiksIHNlcCA9Ilx0Iiwgcm93Lm5hbWVzID0gRikKCm1pY3JvX2Rpdl9kYXRhX2xvbmdlciA8LSBtaWNyb19kaXZfZGF0YSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBPYnNlcnZlZC5yaWNobmVzczpXVS5kaXNwZXJzaW9uLCBuYW1lc190byA9ICJWYXJpYWJsZSIsIHZhbHVlc190byA9ICJWYWx1ZSIpIAp3cml0ZS50YWJsZShtaWNyb19kaXZfZGF0YV9sb25nZXIsIGZpbGUgPSBwYXN0ZTAoZGlyX2RhdGFzZXQsICJtaWNyb19kaXZfZGF0YV9sb25nZXIudHh0IiksIHNlcCA9Ilx0Iiwgcm93Lm5hbWVzID0gRikKYGBgCgoKIyMjIFN0YXRpc3RpY2FsIHRlc3RzIAojIyMjIFQtdGVzdApXZSB1c2VkIHRoZXNlIG1ldHJpY3MgaW4gZWlnaHQgdC10ZXN0cyBhbmQgZm91ciBQRVJNQU5PVkFzIHRvIGNvbXBhcmUgdGhlIGFscGhhIGRpdmVyc2l0eSwgbWljcm9iaWFsIHZhcmlhYmlsaXR5LCBhbmQgbWljcm9iaWFsIGNvbXBvc2l0aW9uIGJldHdlZW4gZmVtYWxlcyB3aXRoIGhpZ2ggb3IgbG93IEFTLgoKQmVmb3JlIHBlcmZvcm1pbmcgdC10ZXN0cywgY2hlY2sgdGhlIHZhcmlhbmNlIGhvbW9nZW5laXR5LiAKCmBgYHtyIEYgdGVzdCBvbiBhbHBoYSwgZXZhbCA9IFR9Cm1pY3JvX2Rpdl9kYXRhIDwtIHJlYWQudGFibGUoZmlsZSA9IHBhc3RlMChkaXJfZGF0YXNldCwgIm1pY3JvX2Rpdl9kYXRhLnR4dCIpLCBzZXAgPSJcdCIsaGVhZGVyID1UICkKIyBBbHBoYSBpbmRpY2VzCnBhc3RlMCgiSG9tb2dlbmVpdHkgb2YgcmljaG5lc3MgYmV0d2VlbiBzdGF0dXMgcC12YWx1ZSA9ICIsIHJvdW5kKHZhci50ZXN0KE9ic2VydmVkLnJpY2huZXNzfiBzdGF0dXMsbWljcm9fZGl2X2RhdGEpJHAudmFsdWUsIDIpKQpwYXN0ZTAoIkhvbW9nZW5laXR5IG9mIFNoYW5ub24gYmV0d2VlbiBzdGF0dXMgcC12YWx1ZSA9ICIsIHJvdW5kKHZhci50ZXN0KFNoYW5ub25+c3RhdHVzLG1pY3JvX2Rpdl9kYXRhKSRwLnZhbHVlLCAyKSkKcGFzdGUwKCJIb21vZ2VuZWl0eSBvZiBGYWl0aCBiZXR3ZWVuIHN0YXR1cyBwLXZhbHVlID0gIiwgcm91bmQodmFyLnRlc3QoRmFpdGh+c3RhdHVzLG1pY3JvX2Rpdl9kYXRhKSRwLnZhbHVlLCAyKSkKcGFzdGUwKCJIb21vZ2VuZWl0eSBvZiBIaWxsMSBiZXR3ZWVuIHN0YXR1cyBwLXZhbHVlID0gIiwgcm91bmQodmFyLnRlc3QoSGlsbDF+c3RhdHVzLG1pY3JvX2Rpdl9kYXRhKSRwLnZhbHVlLCAyKSkKIyBCZXRhIGluZGljZXMKcGFzdGUwKCJIb21vZ2VuZWl0eSBvZiBKYWNjIGRpc3BlcnNpb24gYmV0d2VlbiBzdGF0dXMgcC12YWx1ZSA9ICIsIHJvdW5kKHZhci50ZXN0KEphY2MuZGlzcGVyc2lvbn4gc3RhdHVzLG1pY3JvX2Rpdl9kYXRhKSRwLnZhbHVlLCAyKSkKcGFzdGUwKCJIb21vZ2VuZWl0eSBvZiBCQyBkaXNwZXJzaW9uIGJldHdlZW4gc3RhdHVzIHAtdmFsdWUgPSAiLCByb3VuZCh2YXIudGVzdChCQy5kaXNwZXJzaW9ufiBzdGF0dXMsbWljcm9fZGl2X2RhdGEpJHAudmFsdWUsIDIpKQpwYXN0ZTAoIkhvbW9nZW5laXR5IG9mIFVVIGRpc3BlcnNpb24gYmV0d2VlbiBzdGF0dXMgcC12YWx1ZSA9ICIsIHJvdW5kKHZhci50ZXN0KFVVLmRpc3BlcnNpb25+IHN0YXR1cyxtaWNyb19kaXZfZGF0YSkkcC52YWx1ZSwgMikpCnBhc3RlMCgiSG9tb2dlbmVpdHkgb2YgV1UgZGlzcGVyc2lvbiBiZXR3ZWVuIHN0YXR1cyBwLXZhbHVlID0gIiwgcm91bmQodmFyLnRlc3QoV1UuZGlzcGVyc2lvbn4gc3RhdHVzLG1pY3JvX2Rpdl9kYXRhKSRwLnZhbHVlLCAyKSkKCmBgYAoKYW5kIHByb2NlZWQgdG8gdC10ZXN0IHRvIGNvbXBhcmUgdGhlIGFscGhhIGFuZCBiZXRhIGJldHdlZW4gbGV2ZWxzIG9mIGFnZ3JlZ2F0aW9uCgpgYGB7ciB0IHRlc3RzLCBldmFsID0gVH0KbWljcm9fZGl2X2RhdGEgPC0gcmVhZC50YWJsZShmaWxlID0gcGFzdGUwKGRpcl9kYXRhc2V0LCAibWljcm9fZGl2X2RhdGEudHh0IiksIHNlcCA9Ilx0IixoZWFkZXIgPVQgKQojIEFscGhhIHQgdGVzdApwYXN0ZTAoIlZhcmlhbmNlIGNvbXBhcmlzaW9ucyBvZiByaWNobmVzcyBiZXR3ZWVuIHN0YXR1czogdD0gIixyb3VuZCh0LnRlc3QoT2JzZXJ2ZWQucmljaG5lc3N+c3RhdHVzLG1pY3JvX2Rpdl9kYXRhLCB2YXI9VCkkc3RhdGlzdGljLDIpLCAiOyBkZj0gIiwgdC50ZXN0KE9ic2VydmVkLnJpY2huZXNzfnN0YXR1cyxtaWNyb19kaXZfZGF0YSwgdmFyPVQpJHBhcmFtZXRlciwiOyBwLXZhbHVlPSAiLHJvdW5kKHQudGVzdChPYnNlcnZlZC5yaWNobmVzc35zdGF0dXMsbWljcm9fZGl2X2RhdGEsIHZhcj1UKSRwLnZhbHVlKSkKcGFzdGUwKCJWYXJpYW5jZSBjb21wYXJpc2lvbnMgb2YgU2hhbm5vbiBiZXR3ZWVuIHN0YXR1czogdD0gIixyb3VuZCh0LnRlc3QoU2hhbm5vbn5zdGF0dXMsbWljcm9fZGl2X2RhdGEsIHZhcj1UKSRzdGF0aXN0aWMsMiksICI7IGRmPSAiLCB0LnRlc3QoU2hhbm5vbn5zdGF0dXMsbWljcm9fZGl2X2RhdGEsIHZhcj1UKSRwYXJhbWV0ZXIsIjsgcC12YWx1ZT0gIixyb3VuZCh0LnRlc3QoU2hhbm5vbn5zdGF0dXMsbWljcm9fZGl2X2RhdGEsIHZhcj1UKSRwLnZhbHVlKSkKcGFzdGUwKCJWYXJpYW5jZSBjb21wYXJpc2lvbnMgb2YgRmFpdGggYmV0d2VlbiBzdGF0dXM6IHQ9ICIscm91bmQodC50ZXN0KEZhaXRofnN0YXR1cyxtaWNyb19kaXZfZGF0YSwgdmFyPVQpJHN0YXRpc3RpYywyKSwgIjsgZGY9ICIsIHQudGVzdChGYWl0aH5zdGF0dXMsbWljcm9fZGl2X2RhdGEsIHZhcj1UKSRwYXJhbWV0ZXIsIjsgcC12YWx1ZT0gIixyb3VuZCh0LnRlc3QoRmFpdGh+c3RhdHVzLG1pY3JvX2Rpdl9kYXRhLCB2YXI9VCkkcC52YWx1ZSkpCnBhc3RlMCgiVmFyaWFuY2UgY29tcGFyaXNpb25zIG9mIEhpbGwxIGJldHdlZW4gc3RhdHVzOiB0PSAiLHJvdW5kKHQudGVzdChIaWxsMX5zdGF0dXMsbWljcm9fZGl2X2RhdGEsIHZhcj1UKSRzdGF0aXN0aWMsMiksICI7IGRmPSAiLCB0LnRlc3QoSGlsbDF+c3RhdHVzLG1pY3JvX2Rpdl9kYXRhLCB2YXI9VCkkcGFyYW1ldGVyLCI7IHAtdmFsdWU9ICIscm91bmQodC50ZXN0KEhpbGwxfnN0YXR1cyxtaWNyb19kaXZfZGF0YSwgdmFyPVQpJHAudmFsdWUpKQoKIyBCZXRhIGRpdmVzaXR5CnBhc3RlMCgiVmFyaWFuY2UgY29tcGFyaXNpb25zIG9mIEphY2MuZGlzcGVyc2lvbiBiZXR3ZWVuIHN0YXR1czogdD0gIixyb3VuZCh0LnRlc3QoSmFjYy5kaXNwZXJzaW9ufnN0YXR1cyxtaWNyb19kaXZfZGF0YSwgdmFyPVQpJHN0YXRpc3RpYywyKSwgIjsgZGY9ICIsIHQudGVzdChKYWNjLmRpc3BlcnNpb25+c3RhdHVzLG1pY3JvX2Rpdl9kYXRhLCB2YXI9VCkkcGFyYW1ldGVyLCI7IHAtdmFsdWU9ICIscm91bmQodC50ZXN0KEphY2MuZGlzcGVyc2lvbn5zdGF0dXMsbWljcm9fZGl2X2RhdGEsIHZhcj1UKSRwLnZhbHVlKSkKcGFzdGUwKCJWYXJpYW5jZSBjb21wYXJpc2lvbnMgb2YgQkMuZGlzcGVyc2lvbiBiZXR3ZWVuIHN0YXR1czogdD0gIixyb3VuZCh0LnRlc3QoQkMuZGlzcGVyc2lvbn5zdGF0dXMsbWljcm9fZGl2X2RhdGEsIHZhcj1UKSRzdGF0aXN0aWMsMiksICI7IGRmPSAiLCB0LnRlc3QoQkMuZGlzcGVyc2lvbn5zdGF0dXMsbWljcm9fZGl2X2RhdGEsIHZhcj1UKSRwYXJhbWV0ZXIsIjsgcC12YWx1ZT0gIixyb3VuZCh0LnRlc3QoQkMuZGlzcGVyc2lvbn5zdGF0dXMsbWljcm9fZGl2X2RhdGEsIHZhcj1UKSRwLnZhbHVlKSkKcGFzdGUwKCJWYXJpYW5jZSBjb21wYXJpc2lvbnMgb2YgVVUuZGlzcGVyc2lvbiBiZXR3ZWVuIHN0YXR1czogdD0gIixyb3VuZCh0LnRlc3QoVVUuZGlzcGVyc2lvbn5zdGF0dXMsbWljcm9fZGl2X2RhdGEsIHZhcj1UKSRzdGF0aXN0aWMsMiksICI7IGRmPSAiLCB0LnRlc3QoVVUuZGlzcGVyc2lvbn5zdGF0dXMsbWljcm9fZGl2X2RhdGEsIHZhcj1UKSRwYXJhbWV0ZXIsIjsgcC12YWx1ZT0gIixyb3VuZCh0LnRlc3QoVVUuZGlzcGVyc2lvbn5zdGF0dXMsbWljcm9fZGl2X2RhdGEsIHZhcj1UKSRwLnZhbHVlKSkKcGFzdGUwKCJWYXJpYW5jZSBjb21wYXJpc2lvbnMgb2YgV1UuZGlzcGVyc2lvbiBiZXR3ZWVuIHN0YXR1czogPSAiLHJvdW5kKHQudGVzdChXVS5kaXNwZXJzaW9ufnN0YXR1cyxtaWNyb19kaXZfZGF0YSwgdmFyPUYpJHN0YXRpc3RpYywyKSwgIjsgZGY9ICIsIHQudGVzdChXVS5kaXNwZXJzaW9ufnN0YXR1cyxtaWNyb19kaXZfZGF0YSwgdmFyPUYpJHBhcmFtZXRlciwiOyBwLXZhbHVlPSAiLHJvdW5kKHQudGVzdChXVS5kaXNwZXJzaW9ufnN0YXR1cyxtaWNyb19kaXZfZGF0YSwgdmFyPUYpJHAudmFsdWUpKSAjIHdlbGNoIHRlc3QgYmVjYXVzZSB2YXJpYW5jZXMgd2VyZSBub3QgaG9tb2dlbmVvdXMKYGBgCgpgYGB7ciBib3hwbG90ICwgZXZhbCA9IFQsIHJlc3VsdHM9RiwgbWVzc2FnZT0gRkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy53aWR0aD0gMTIsIGZpZy5oZWlnaHQ9IDl9Cm1pY3JvX2Rpdl9kYXRhX2xvbmdlciA8LSByZWFkLnRhYmxlKGZpbGUgPSBwYXN0ZTAoZGlyX2RhdGFzZXQsICJtaWNyb19kaXZfZGF0YV9sb25nZXIudHh0IiksIHNlcCA9Ilx0IiwgaGVhZGVyID0gVCkKbWljcm9fZGl2X2RhdGFfbG9uZ2VyJFZhcmlhYmxlIDwtIGZhY3RvcihtaWNyb19kaXZfZGF0YV9sb25nZXIkVmFyaWFibGUsIGxldmVscyA9IGMoIk9ic2VydmVkLnJpY2huZXNzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTaGFubm9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJGYWl0aCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSGlsbDEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkphY2MuZGlzcGVyc2lvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQkMuZGlzcGVyc2lvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVVUuZGlzcGVyc2lvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiV1UuZGlzcGVyc2lvbiIpKQptaWNyb19kaXZfZGF0YV9sb25nZXIkc3RhdHVzIDwtIGZhY3RvcihtaWNyb19kaXZfZGF0YV9sb25nZXIkc3RhdHVzICwgbGV2ZWxzID0gYygiTG93IiwgIkhpZ2giKSkgIAoKRmlnX2JveHBsb3QgPC0gbWljcm9fZGl2X2RhdGFfbG9uZ2VyICU+JQogIGdncGxvdChhZXMoeCA9IHN0YXR1cyAsIHkgPSBWYWx1ZSwgY29sb3IgPSBzdGF0dXMpKSArCiAgZ2VvbV9qaXR0ZXIocG9zaXRpb24gPSBwb3NpdGlvbl9qaXR0ZXIod2lkdGggPSAuMjApLCBhbHBoYSA9IDAuNSwgc2l6ZSA9IDMpICsgCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoImRhcmtibHVlIiAsICJkYXJrcmVkIikpICsKICB0aGVtZV9idygpICsKICBnZW9tX2JveHBsb3QoYWxwaGE9MC4xLCBvdXRsaWVyLmNvbG91ciA9IE5BKSArIAogIHRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZT0xOCwgZmFtaWx5ID0gInNlcmlmIiksCiAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJzZXJpZiIsc2l6ZSA9IDE0KSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiLHNpemUgPSAxOCwgYW5nbGUgPSAwKSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemU9MTQsIGZhbWlseSA9ICJzZXJpZiIpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsgCiAgZmFjZXRfd3JhcCggfiBWYXJpYWJsZSwgbnJvdz0yLCBuY29sID0gNCwgc2NhbGVzID0gImZyZWUiKSArCiAgZ2VvbV9zaWduaWYoY29tcGFyaXNvbnMgPSBsaXN0KGMoIkxvdyIsICJIaWdoIikpLCAKICAgICAgICAgICAgICBtYXBfc2lnbmlmX2xldmVsID0gVCwgdGV4dHNpemU9NCwgY29sb3I9ImJsYWNrIiwgZmFtaWx5ID0gInNlcmlmIiwgdmp1c3QgPSAwKQpGaWdfYm94cGxvdCAKcGRmKGZpbGUgPSBwYXN0ZTAoZGlyX2dyYXBoLCAiZmlnX2JveHBsb3QucGRmIiksIGhlID0gOSwgd2kgPSAxMikKRmlnX2JveHBsb3QKZGV2Lm9mZigpCmBgYAoKIyMjIyBQRVJNQU5PVkFzIGFuZCBQQ29BCkZpbmFsbHksIHdlIHVzZWQgZm91ciBQRVJNQU5PVkFzIHRvIHRlc3QgdGhlIGluZmx1ZW5jZSBvZiB0aGUgYWdncmVnYXRpb24gc3RhdHVzIG9uIHRoZSBtaWNyb2JpYWwgY29tcG9zaXRpb24KCmBgYHtyIFBFUk1BTk9WQSwgZXZhbCA9VH0KcGFzdGUwKCJQRVJNQU5PVkEgb24gSmFjY2FyZCBkaXN0YW5jZSBSMj0gIiwgcm91bmQoYWRvbmlzMihvdHUuamFjYyB+IHNhbXBfZGF0YSRzdGF0dXMpJFIyWzFdLDIpICwgIjsgcC12YWx1ZT0gIixyb3VuZChhZG9uaXMyKG90dS5qYWNjIH4gc2FtcF9kYXRhJHN0YXR1cykkYFByKD5GKWBbMV0sMikpCnBhc3RlMCgiUEVSTUFOT1ZBIG9uIEJDIGRpc3RhbmNlIFIyPSAiLCByb3VuZChhZG9uaXMyKG90dS5iYyB+IHNhbXBfZGF0YSRzdGF0dXMpJFIyWzFdLDIpICwgIjsgcC12YWx1ZT0gIixyb3VuZChhZG9uaXMyKG90dS5iYyB+IHNhbXBfZGF0YSRzdGF0dXMpJGBQcig+RilgWzFdLDIpKQpwYXN0ZTAoIlBFUk1BTk9WQSBvbiBVVSBkaXN0YW5jZSBSMj0gIiwgcm91bmQoYWRvbmlzMihvdHUudXUgfiBzYW1wX2RhdGEkc3RhdHVzKSRSMlsxXSwyKSAsICI7IHAtdmFsdWU9ICIscm91bmQoYWRvbmlzMihvdHUudXUgfiBzYW1wX2RhdGEkc3RhdHVzKSRgUHIoPkYpYFsxXSwyKSkKcGFzdGUwKCJQRVJNQU5PVkEgb24gV1UgZGlzdGFuY2UgUjI9ICIsIHJvdW5kKGFkb25pczIob3R1Lnd1IH4gc2FtcF9kYXRhJHN0YXR1cykkUjJbMV0sMikgLCAiOyBwLXZhbHVlPSAiLHJvdW5kKGFkb25pczIob3R1Lnd1IH4gc2FtcF9kYXRhJHN0YXR1cykkYFByKD5GKWBbMV0sMikpCmBgYAoKYGBge3IgUENvQSwgZXZhbCA9IFQsIHJlc3VsdHM9J2hpZGUnLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBmaWcud2lkdGg9IDEyLCBmaWcuaGVpZ2h0PTEyfQojICAqSmFjY2FyZCAKcGNvYS5zdWIuamFjYyA8LSBwY29hKG90dS5qYWNjKQpwY29hX2Nvb3JkLmphY2MgPC0gcGNvYS5zdWIuamFjYyR2ZWN0b3JzWywxOjNdCmh1bGwuamFjYyA8LSBjYmluZChwY29hX2Nvb3JkLmphY2MsIHNhbXBfZGF0YSkgIyBDb250cnVjdGlvbiBvZiB0aGUgdGFibGUgZm9yIGdyYXBoaWMgCgpwY29hLmphY2MgPC0gZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoZGF0YSA9IGh1bGwuamFjYywgYWVzKHg9QXhpcy4xLCB5PUF4aXMuMiwgc2l6ZSA9IHN0YXR1cywgY29sb3IgPSBzdGF0dXMpLCBhbHBoYSA9IDAuNywgc2hhcGUgPSAxNikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdD0wLCBjb2xvdXI9ImxpZ2h0Z3JleSIsIGxpbmV0eXBlID0gMikgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdD0wLCBjb2xvdXI9ImxpZ2h0Z3JleSIsIGxpbmV0eXBlID0gMikgKwogIHhsYWIocGFzdGUoIlBDbzEgKCIsIHJvdW5kKHBjb2Euc3ViLmphY2MkdmFsdWVzJFJlbGF0aXZlX2VpZ1sxXSoxMDAsIDEpLCAiJSkiKSkgKwogIHlsYWIocGFzdGUoIlBDbzIgKCIsIHJvdW5kKHBjb2Euc3ViLmphY2MkdmFsdWVzJFJlbGF0aXZlX2VpZ1syXSoxMDAsIDEpLCAiJSkiKSkgICsKICB0aGVtZV9idygpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiZGFya2JsdWUiLCAiZGFya3JlZCIpKSsKICBjb29yZF9lcXVhbCgpICsKICB0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZT0xNCwgZmFtaWx5ID0gInNlcmlmIiksICMgcmVtb3ZlIHgtYXhpcyBsYWJlbHMKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZT0xNCwgZmFtaWx5ID0gInNlcmlmIiksICMgcmVtb3ZlIHktYXhpcyBsYWJlbHMKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPTE0LCBmYW1pbHkgPSAic2VyaWYiKSwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplPTE0LCBmYW1pbHkgPSAic2VyaWYiKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksICAjcmVtb3ZlIG1ham9yLWdyaWQgbGFiZWxzCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwgICNyZW1vdmUgbWlub3ItZ3JpZCBsYWJlbHMKICAgICAgICBwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiLCBzaXplID0gMjIpLCAKICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMCwgZmFtaWx5ID0gInNlcmlmIiksCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAwLGZhbWlseSA9ICJzZXJpZiIpKSsKICBsYWJzKHRpdGxlID0gIlBDb0Egb24gSmFjY2FyZCBkaXN0YW5jZXMiLCAKICAgICAgIHNpemUgPSAiU3RhdHVzIiwgY29sb3IgPSAiU3RhdHVzIikKCiMgKiBCcmF5LUN1cnRpcyAKcGNvYS5zdWIuYmMgPC0gcGNvYShvdHUuYmMpCnBjb2FfY29vcmQuYmMgPC0gcGNvYS5zdWIuYmMkdmVjdG9yc1ssMTozXQpodWxsLmJjIDwtIGNiaW5kKHBjb2FfY29vcmQuYmMsIHNhbXBfZGF0YSkgIyBDb250cnVjdGlvbiBvZiB0aGUgdGFibGUgZm9yIGdyYXBoaWMgCgpwY29hLmJjIDwtIGdncGxvdCgpICsKICBnZW9tX3BvaW50KGRhdGEgPSBodWxsLmJjLCBhZXMoeD1BeGlzLjEsIHk9QXhpcy4yLCBzaXplID0gc3RhdHVzLCBjb2xvciA9IHN0YXR1cyksIGFscGhhID0gMC43LCBzaGFwZSA9IDE2KSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTAsIGNvbG91cj0ibGlnaHRncmV5IiwgbGluZXR5cGUgPSAyKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PTAsIGNvbG91cj0ibGlnaHRncmV5IiwgbGluZXR5cGUgPSAyKSArCiAgeGxhYihwYXN0ZSgiUENvMSAoIiwgcm91bmQocGNvYS5zdWIuYmMkdmFsdWVzJFJlbGF0aXZlX2VpZ1sxXSoxMDAsIDEpLCAiJSkiKSkgKwogIHlsYWIocGFzdGUoIlBDbzIgKCIsIHJvdW5kKHBjb2Euc3ViLmJjJHZhbHVlcyRSZWxhdGl2ZV9laWdbMl0qMTAwLCAxKSwgIiUpIikpICArCiAgdGhlbWVfYncoKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoImRhcmtibHVlIiwgImRhcmtyZWQiKSkrCiAgY29vcmRfZXF1YWwoKSArCiAgdGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemU9MTQsIGZhbWlseSA9ICJzZXJpZiIpLCAjIHJlbW92ZSB4LWF4aXMgbGFiZWxzCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemU9MTQsIGZhbWlseSA9ICJzZXJpZiIpLCAjIHJlbW92ZSB5LWF4aXMgbGFiZWxzCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZT0xNCwgZmFtaWx5ID0gInNlcmlmIiksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZT0xNCwgZmFtaWx5ID0gInNlcmlmIiksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCAgI3JlbW92ZSBtYWpvci1ncmlkIGxhYmVscwogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksICAjcmVtb3ZlIG1pbm9yLWdyaWQgbGFiZWxzCiAgICAgICAgcGxvdC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gInNlcmlmIiwgc2l6ZSA9IDIyKSwgCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDAsIGZhbWlseSA9ICJzZXJpZiIpLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMCxmYW1pbHkgPSAic2VyaWYiKSkrCiAgbGFicyh0aXRsZSA9ICJQQ29BIG9uIEJyYXktQ3VydGlzIGRpc3RhbmNlcyIsIAogICAgICAgc2l6ZSA9ICJTdGF0dXMiLCBjb2xvciA9ICJTdGF0dXMiKQoKIyAqIFVud2VpZ2h0ZWQgVW5pZnJhYwpwY29hLnN1Yi51dSA8LSBwY29hKG90dS51dSkKcGNvYV9jb29yZC51dSA8LSBwY29hLnN1Yi51dSR2ZWN0b3JzWywxOjNdCmh1bGwudXUgPC0gY2JpbmQocGNvYV9jb29yZC51dSwgc2FtcF9kYXRhKQoKcGNvYS51dSA8LSBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChkYXRhID0gaHVsbC51dSwgYWVzKHg9QXhpcy4xLCB5PUF4aXMuMiwgc2l6ZSA9IHN0YXR1cywgY29sb3IgPSBzdGF0dXMpLCBhbHBoYSA9IDAuNywgc2hhcGUgPSAxNikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdD0wLCBjb2xvdXI9ImxpZ2h0Z3JleSIsIGxpbmV0eXBlID0gMikgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdD0wLCBjb2xvdXI9ImxpZ2h0Z3JleSIsIGxpbmV0eXBlID0gMikgKwogIHhsYWIocGFzdGUoIlBDbzEgKCIsIHJvdW5kKHBjb2Euc3ViLnV1JHZhbHVlcyRSZWxhdGl2ZV9laWdbMV0qMTAwLCAxKSwgIiUpIikpICsKICB5bGFiKHBhc3RlKCJQQ28yICgiLCByb3VuZChwY29hLnN1Yi51dSR2YWx1ZXMkUmVsYXRpdmVfZWlnWzJdKjEwMCwgMSksICIlKSIpKSAgKwogIHRoZW1lX2J3KCkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJkYXJrYmx1ZSIsICJkYXJrcmVkIikpKwogIGNvb3JkX2VxdWFsKCkgKwogIHRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplPTE0LCBmYW1pbHkgPSAic2VyaWYiKSwgIyByZW1vdmUgeC1heGlzIGxhYmVscwogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplPTE0LCBmYW1pbHkgPSAic2VyaWYiKSwgIyByZW1vdmUgeS1heGlzIGxhYmVscwogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemU9MTQsIGZhbWlseSA9ICJzZXJpZiIpLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemU9MTQsIGZhbWlseSA9ICJzZXJpZiIpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgICNyZW1vdmUgbWFqb3ItZ3JpZCBsYWJlbHMKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLCAgI3JlbW92ZSBtaW5vci1ncmlkIGxhYmVscwogICAgICAgIHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJzZXJpZiIsIHNpemUgPSAyMiksIAogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAwLCBmYW1pbHkgPSAic2VyaWYiKSwKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDAsZmFtaWx5ID0gInNlcmlmIikpKwogIGxhYnModGl0bGUgPSAiUENvQSBvbiBVbndlaWdodGVkIFVuaWZyYWMgZGlzdGFuY2VzIiwgCiAgICAgICBzaXplID0gIlN0YXR1cyIsIGNvbG9yID0gIlN0YXR1cyIpCgoKIyAqIFdlaWdodGVkIFVuaWZyYWMgCnBjb2Euc3ViLnd1IDwtIHBjb2Eob3R1Lnd1KQpwY29hX2Nvb3JkLnd1IDwtIHBjb2Euc3ViLnd1JHZlY3RvcnNbLDE6M10KaHVsbC53dSA8LSBjYmluZChwY29hX2Nvb3JkLnd1LCBzYW1wX2RhdGEpCgpwY29hLnd1IDwtIGdncGxvdCgpICsKICBnZW9tX3BvaW50KGRhdGEgPSBodWxsLnd1LCBhZXMoeD1BeGlzLjEsIHk9QXhpcy4yLCBzaXplID0gc3RhdHVzLCBjb2xvciA9IHN0YXR1cyksIGFscGhhID0gMC43LCBzaGFwZSA9IDE2KSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTAsIGNvbG91cj0ibGlnaHRncmV5IiwgbGluZXR5cGUgPSAyKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PTAsIGNvbG91cj0ibGlnaHRncmV5IiwgbGluZXR5cGUgPSAyKSArCiAgeGxhYihwYXN0ZSgiUENvMSAoIiwgcm91bmQocGNvYS5zdWIud3UkdmFsdWVzJFJlbGF0aXZlX2VpZ1sxXSoxMDAsIDEpLCAiJSkiKSkgKwogIHlsYWIocGFzdGUoIlBDbzIgKCIsIHJvdW5kKHBjb2Euc3ViLnd1JHZhbHVlcyRSZWxhdGl2ZV9laWdbMl0qMTAwLCAxKSwgIiUpIikpICArCiAgdGhlbWVfYncoKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoImRhcmtibHVlIiwgImRhcmtyZWQiKSkrCiAgY29vcmRfZXF1YWwoKSArCiAgdGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemU9MTQsIGZhbWlseSA9ICJzZXJpZiIpLCAjIHJlbW92ZSB4LWF4aXMgbGFiZWxzCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemU9MTQsIGZhbWlseSA9ICJzZXJpZiIpLCAjIHJlbW92ZSB5LWF4aXMgbGFiZWxzCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZT0xNCwgZmFtaWx5ID0gInNlcmlmIiksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZT0xNCwgZmFtaWx5ID0gInNlcmlmIiksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCAgI3JlbW92ZSBtYWpvci1ncmlkIGxhYmVscwogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksICAjcmVtb3ZlIG1pbm9yLWdyaWQgbGFiZWxzCiAgICAgICAgcGxvdC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gInNlcmlmIiwgc2l6ZSA9IDIyKSwgCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDAsIGZhbWlseSA9ICJzZXJpZiIpLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMCxmYW1pbHkgPSAic2VyaWYiKSkrCiAgbGFicyh0aXRsZSA9ICJQQ29BIG9uIFdlaWdodGVkIFVuaWZyYWMgZGlzdGFuY2VzIiwgCiAgICAgICBzaXplID0gIlN0YXR1cyIsIGNvbG9yID0gIlN0YXR1cyIpCgpsZWdlbmQgPC0gZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoZGF0YSA9IGh1bGwud3UsIGFlcyh4PUF4aXMuMSwgeT1BeGlzLjIsIHNpemUgPSBzdGF0dXMsIGNvbG9yID0gc3RhdHVzKSwgYWxwaGEgPSAwLjcsIHNoYXBlID0gMTYpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MCwgY29sb3VyPSJsaWdodGdyZXkiLCBsaW5ldHlwZSA9IDIpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9MCwgY29sb3VyPSJsaWdodGdyZXkiLCBsaW5ldHlwZSA9IDIpICsKICB4bGFiKHBhc3RlKCJQQ28xICgiLCByb3VuZChwY29hLnN1Yi53dSR2YWx1ZXMkUmVsYXRpdmVfZWlnWzFdKjEwMCwgMSksICIlKSIpKSArCiAgeWxhYihwYXN0ZSgiUENvMiAoIiwgcm91bmQocGNvYS5zdWIud3UkdmFsdWVzJFJlbGF0aXZlX2VpZ1syXSoxMDAsIDEpLCAiJSkiKSkgICsKICB0aGVtZV9idygpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiZGFya2JsdWUiLCAiZGFya3JlZCIpKSsKICBjb29yZF9lcXVhbCgpICsKICB0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZT0xMiwgZmFtaWx5ID0gInNlcmlmIiksICMgcmVtb3ZlIHgtYXhpcyBsYWJlbHMKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZT0xMiwgZmFtaWx5ID0gInNlcmlmIiksICMgcmVtb3ZlIHktYXhpcyBsYWJlbHMKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPTEyLCBmYW1pbHkgPSAic2VyaWYiKSwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplPTEyLCBmYW1pbHkgPSAic2VyaWYiKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksICAjcmVtb3ZlIG1ham9yLWdyaWQgbGFiZWxzCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwgICNyZW1vdmUgbWlub3ItZ3JpZCBsYWJlbHMKICAgICAgICBwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiLCBzaXplID0gMjIpLCAKICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGZhbWlseSA9ICJzZXJpZiIpLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsZmFtaWx5ID0gInNlcmlmIikpKwogIGxhYnModGl0bGUgPSAiUENvQSBvbiBXZWlnaHRlZCBVbmlmcmFjIGRpc3RhbmNlcyIsIAogICAgICAgc2l6ZSA9ICJTdGF0dXMiLCBjb2xvciA9ICJTdGF0dXMiKQoKZ2dhcnJhbmdlKGxlZ2VuZC5ncm9iID0gZ2V0X2xlZ2VuZChsZWdlbmQpLAogICAgICAgICAgbGVnZW5kID0gInJpZ2h0IiwKICAgICAgICAgIHBjb2EuamFjYyxwY29hLmJjLAogICAgICAgICAgcGNvYS53dSxwY29hLnV1LCBuY29sID0gMiwgbnJvdyA9IDIpCnBkZihmaWxlID0gcGFzdGUwKGRpcl9ncmFwaCwgIlBDb0EucGRmIiksIGhlID0gMTIsIHdpID0gMTIpCmdnYXJyYW5nZShsZWdlbmQuZ3JvYiA9IGdldF9sZWdlbmQobGVnZW5kKSwKICAgICAgICAgIGxlZ2VuZCA9ICJyaWdodCIsCiAgICAgICAgICBwY29hLmphY2MscGNvYS5iYywKICAgICAgICAgIHBjb2Eud3UscGNvYS51dSwgbmNvbCA9IDIsIG5yb3cgPSAyKQpkZXYub2ZmKCkKYGBgCgojIyBQYXJ0IElWOiBHdXQgdHJhbnNwbGFudGF0aW9uCltiYWNrIHRvIHRvcF0oI2JhY2sgdG8gdG9wKQoKTGFzdGx5LCBkb3dubG9hZCB0aGUgZGF0YSBvZiBzYW1wbGVzIGludm9sdmVkIGluIHRoZSBndXQgdHJhbnNwbGFudCB3aXRoIHRoZWlyIEFTIGJlZm9yZSAKdHJhbnNwbGFudCBhbmQgYWZ0ZXIgdHJhbnNwbGFudCwgd2l0aCB0aGVpciByZXNwZWN0aXZlIGNvbWJpbmF0aW9ucwoKYGBge3IgZmlsZSBsb2FkIHRyYW5zcGxhbnQsIGV2YWwgPSBUfQpkYXRhX3JlY2lwaWVudCA8LSByZWFkLnRhYmxlKGZpbGUgPSBwYXN0ZTAoZGlyX2RhdGFzZXQsICJhZ2dyZWdhdGlvbl9hZnRlcl90cmFuc3BsYW50LnR4dCIpLCBzZXAgPSAiXHQiLCBoZWFkZXIgPSBUKQpkYXRhX3RyYW5zcGxhbnQgPC0gcmVhZC50YWJsZShmaWxlID0gcGFzdGUwKGRpcl9kYXRhc2V0LCAidHJhbnNwbGFudF9kYXRhLnR4dCIpLCBzZXAgPSAiXHQiLCBoZWFkZXIgPSBUKQpgYGAKCkFzIG1hZGUgZm9yIHRoZSAqKkFTX2RhdGEgZmlsZSoqIHdlIGNyZWF0ZSAqKkFTX3RyYW5zcGxhbnQqKiB3aXRoIHRoZSBBUzcyIGFmdGVyIGd1dCB0cmFuc3BsYW50IGFuZCBqb2luIGl0IHRvIHRoZSBwcmV2aW91cyBpbmZvcm1hdGlvbiBjb25jZXJuaW5nIHRoZSAqKmd1dCBkb25vcioqIGFuZCAqKmd1dCByZWNpcGllbnQqKiB3aXRoIHRoZWlyIHJlc3BlY3RpdmUgKiphZ2dyZWdhdGlvbiBsZXZlbCoqLgoKYGBge3IgQVMgZGF0YSB0cmFuc3BsYW50IGZpbGUsICBldmFsID0gVCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KZGF0YV90cmFuc3BsYW50IDwtIHJlYWQudGFibGUoZmlsZSA9IHBhc3RlMChkaXJfZGF0YXNldCwgInRyYW5zcGxhbnRfZGF0YS50eHQiKSwgc2VwID0gIlx0IiwgaGVhZGVyID0gVCkKQVNfdHJhbnNwbGFudCA8LSBsZWZ0X2pvaW4oZGF0YV90cmFuc3BsYW50LAogICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhX3JlY2lwaWVudCAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXBfYnkoSURfUmVjaXBpZW50LGRheSkgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZShBZ2dyZWdhdGlvbiA9IHN1bShhdHRyYWN0aW9uKSkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IGRheSwgdmFsdWVzX2Zyb20gPSBBZ2dyZWdhdGlvbikgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbXV0YXRlKEFTNzJfYWZ0ZXIgPSBzdW0oY19hY3Jvc3MoRDFfYWZ0ZXI6RDNfYWZ0ZXIpKSkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXMuZGF0YS5mcmFtZSgpLAogICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9ICJJRF9SZWNpcGllbnQiKSAlPiUKICBtdXRhdGUoRGVsdGEgPSBBUzcyX2FmdGVyLUFTNzJfYmVmb3JlKSAjIENyZWF0ZSB0aGUgZGVsdGEgdmFyaWF0aW9uIG9mIHRoZSBBUwoKd3JpdGUudGFibGUoQVNfdHJhbnNwbGFudCAsIGZpbGUgPSBwYXN0ZTAoZGlyX2RhdGFzZXQsICJBU190cmFuc3BsYW50LnR4dCIpLCBzZXAgPSAiXHQiLCByb3cubmFtZXMgPSBGKQpgYGAKCkZpbmFsbHksIHdlIGFuYWx5c2VkIGRhdGEgZnJvbSB0aGUgdHJhbnNwbGFudCBleHBlcmltZW50IHVzaW5nIGEgTE0sIGluIHdoaWNoIHdlIGVudGVyZWQgdGhlIEFTIG1lYXN1cmVkIGFmdGVyIGd1dCB0cmFuc3BsYW50IGFzIGEgcmVzcG9uc2UgdmFyaWFibGUsIHdoaWxlIHdlIHVzZWQgdGhlIGFnZ3JlZ2F0aW9uIHN0YXR1cyAoKmkuZS4sKiBoaWdoIG9yIGxvdyBBUykgb2YgdGhlIHJlY2lwaWVudCBmZW1hbGUgYmVmb3JlIGd1dCB0cmFuc3BsYW50LCB0aGUgYWdncmVnYXRpb24gc3RhdHVzIG9mIHRoZSBkb25vciBmZW1hbGUgYmVmb3JlIHRyYW5zcGxhbnQgYW5kIHRoZWlyIGludGVyYWN0aW9uIGFzIGV4cGxhbmF0b3J5IHZhcmlhYmxlcy4KCgpgYGB7ciBsbSB0cmFuc3BsYW50LCBldmFsID0gVCwgcmVzdWx0cz0naGlkZSd9CkFTX3RyYW5zcGxhbnQgPC0gcmVhZC50YWJsZShmaWxlID0gcGFzdGUwKGRpcl9kYXRhc2V0LCAiQVNfdHJhbnNwbGFudC50eHQiKSwgc2VwID0gIlx0IixoZWFkZXI9VCkKQVNfdHJhbnNwbGFudCRDb3VwbGUgPC0gZmFjdG9yKEFTX3RyYW5zcGxhbnQkQ291cGxlLCBsZXZlbHMgPSBjKCJMb3dfTG93IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJIaWdoX0xvdyIpKQoKbG1fdHJhbnNwbGFudCA8LSBsbShBUzcyX2FmdGVyIH4gIEFTNzJfRG9ub3IsIAogICAgICAgICAgICAgICAgICBkYXRhID0gQVNfdHJhbnNwbGFudCkKc2ltdWxhdGlvbk91dHB1dCA8LSBzaW11bGF0ZVJlc2lkdWFscyhmaXR0ZWRNb2RlbCA9IGxtX3RyYW5zcGxhbnQsIHBsb3QgPSBUKQp0ZXN0UmVzaWR1YWxzKGxtX3RyYW5zcGxhbnQpCmNhcjo6QW5vdmEobG1fdHJhbnNwbGFudCkKCnQudGVzdChEZWx0YX4gQ291cGxlLAogICAgICAgQVNfdHJhbnNwbGFudCAlPiUgZmlsdGVyKENvdXBsZSAlaW4lIGMoIkxvd19Mb3ciLCAiSGlnaF9Mb3ciKSksIAogICAgICAgdmFyPVQpCmBgYAoKYGBge3IgZ3JhcGgsIGV2YWwgPSBULCByZXN1bHRzPSdoaWRlJ30KQVNfdHJhbnNwbGFudCA8LSByZWFkLnRhYmxlKGZpbGUgPSBwYXN0ZTAoZGlyX2RhdGFzZXQsICJBU190cmFuc3BsYW50LnR4dCIpLCBzZXAgPSAiXHQiLGhlYWRlcj1UKQpBU190cmFuc3BsYW50JENvdXBsZSA8LSBmYWN0b3IoQVNfdHJhbnNwbGFudCRDb3VwbGUsIGxldmVscyA9IGMoIkxvd19Mb3ciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkhpZ2hfTG93IikpCmNvbG5hbWVzKEFTX3RyYW5zcGxhbnQpW2MoMTMsMTcpXSA8LWNvbG5hbWVzKEFTX3RyYW5zcGxhbnQpW2MoMTMsMTcpXSA8LSBjKCJCZWZvcmUiLCAiQWZ0ZXIiKQoKCkZpZ190cmFuc3BsYW50IDwtIGdncGFpcmVkKEFTX3RyYW5zcGxhbnQgJT4lIGZpbHRlcihDb3VwbGUgJWluJSBjKCJMb3dfTG93IiwgIkhpZ2hfTG93IikpLCAKICAgICAgICAgY29uZDEgPSAiQmVmb3JlIiwgY29uZDIgPSAiQWZ0ZXIiLAogICAgICAgICBsaW5lLmNvbG9yID0gImdyYXkiLCAKICAgICAgICAgbGluZS5zaXplID0gMC40LAogICAgICAgICBwb2ludC5zaXplID0gMCwKICAgICAgICAgZmlsbCA9ICJjb25kaXRpb24iLAogICAgICAgICBwYWxldHRlID0gYygiYWxpY2VibHVlIiwiYXp1cmUzIikpICsKICBnZW9tX3BvaW50KGFscGhhID0gMC41LCBzaXplID0gMykgKwogIHRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gInNlcmlmIixzaXplID0gMTYpLAogICAgICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiLHNpemUgPSAxNiksCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gInNlcmlmIixzaXplID0gMTYpLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJzZXJpZiIsc2l6ZSA9IDE2KSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPTE1LCBmYW1pbHkgPSAic2VyaWYiKSkrCiAgZmFjZXRfZ3JpZCh+IENvdXBsZSkrCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMi41OSwgbGluZXR5cGU9ImRvdHRlZCIsIGNvbG9yID0gImJsYWNrIiwgc2l6ZT0uNSkrCiAgbGFicyh5ID0gIkFnZ3JlZ2F0aW9uIFNjb3JlIDcyaCIpCgpGaWdfdHJhbnNwbGFudAoKcGRmKGZpbGUgPSBwYXN0ZTAoZGlyX2dyYXBoLCAiRmlnX3RyYW5zcGxhbnQucGRmIiksIGhlID0gNywgd2kgPSAxMCkKRmlnX3RyYW5zcGxhbnQKZGV2Lm9mZigpCmBgYAoK